“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情”
接Vue2、Vue3知识总结---完整版(上)✨
为具名插槽提供内容
在向具名插槽提供内容的时候,我们可以在一个元素上使用
v-slot指令,并以 v-slot 的参数的形式提供其名称。
v-slot:header 只能加给 <template> <template> 只起到包裹作用
<my-com-2>
<template v-slot:header>
<h1>ittle<h1>
</template>
<template v-slot:default>
<h1>ittle<h1>
</template>
</my-com-2>
- 具名插槽的简写形式
v-slot:=>#- 外面没有 template包裹只能写
slot="slotName"
作用域插槽
在封装组件的过程中,可以为预留的 插槽绑定 props 数据,这种带有 props 数据的 叫做“作用域插槽”。
组件(用 slot传)传数据给插槽使用者(用 template接收)
插槽使用者接收
- 匿名插槽
<template scope="xx{games}">- 只能有一个
- 具名插槽
<tempalte #header="{msg,user}">
<slot v-for="item in list" :user="item" msg="hello"></slot>
使用作用域插槽
可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据。
<template #header="scope"> {{scope}} // 使用作用域插槽的数据 </template> 直接接受一个对象 scope
解构插槽 Prop
作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。(子传父)
<template #author="{msg,user}">
<h3>一行</h3>
<p>{{msg}}</p>
<p>{{user.name}}</p>
</template>
自定义指令
私有自定义指令
当指令第一次被绑定到元素上的时候,会立即触发 bind()
1 在 directives 节点下声明私有自定义指令。
directives:{
color:{
bind(el){
// el 是绑定了此指令的、原生的 DOM 对象
el.style.color = 'red'
}
}
}
2 使用指令
<h1 v-color> APP 组件 </h1>
为自定义指令 动态绑定参数值
data(){
return {
color:'red'
}
}
<h1 v-color="color">App 组件</h1>
通过 binding 获取指令的参数值
在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值:
directives:{
color:{
bind(el,binding){
// 通过 binging 对象的 .value 属性,获取动态的参数值
// 标签上写 v-color="'red'"/ v-color="color" 都可
el.style.color = binding.value
}
}
}
update 函数
bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发。 update 函数会在每次 DOM 更新时被调用。
directives:{
color:{
// 当指令第一次被绑定到元素时被调用(必写)
bind(el,binding){
// 通过 binging 对象的 .value 属性,获取动态的参数值
el.style.color = binding.value
},
// 每次 DOM 更新时被调用
update(el,binding){
el.style.color = binding.value
}
}
}
函数简写
如果 bind 和 update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
directives:{
color(el,binding){ // 'big-number'(el,binding)
el.style.color = binding.value
}
}
}
全局自定义指令
全局共享的自定义指令需要通过“Vue.directive()”进行声明
Vue.directive('color',function(el,binding){
el.style.color = binding.value
})
//定义全局指令
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:1
},
directives:{
//big 函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
/* 'big-number'(element,binding){
// console.log('big')
element.innerText = binding.value * 10
}, */
big(element,binding){
console.log('big',this) //注意此处的 this 是 window
// console.log('big')
element.innerText = binding.value * 10
},
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
// 此处的 this 为 window
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}
})
参数:
el:是绑定了此指令的、原生的 DOM 对象
bing: 指令核心对象,描述指令全部信息属性。通过 bing 对象的 .value 属性,获取动态的参数值 {value} = bing
name:指令名
value:指令的绑定值
oldValue:指令绑定的前一个值,仅在 update和 componentUpdated钩子中可用。
expression:绑定值的字符串形式。
arg:传给指令的参数
modifers:modifiers:一个包含修饰符的对象。
vnode 虚拟节点
oldVnode:上一个虚拟节点(更新钩子函数中才有用)
ESLint
约束代码风格,可组装的JavaScript 和 JSX检查工具
ctrl + F 可以 查找规则
路由1
前端路由的概念与原理
路由(英文:router)就是对应关系。
SPA 与前端路由
在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成
前端路由
Hash 地址与组件之间的对应关系。
前端路由:key是路径,value是组件。
锚链接 #
- 都属于Hash地址
<a href="#b1">b1</a><div id="b1"></div>
前端路由的工作方式
① 用户点击了页面上的路由链接
② 导致了 URL 地址栏中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
实现简易的前端路由
1 通过 <component> 标签,结合 comName 动态渲染组件
<component :is="comName"></component>
export default{
name:'App',
data(){
return{
comName:'Home'
}
}
}
2 在 App.vue 组件中,为 链接添加对应的 hash 值:
<a href="#/home">Home</a>
<a href="#/movie">Home</a>
<a href="#/about">Home</a>
3 在 created 生命周期函数中,监听浏览器地址栏中 hash 地址的变化,动态切换要展示的组件的名称:
created(){
window.onhashchange = () =>{
switch(location.hash){
case '#home': // 点击了首页链接
this.comName = 'Home'
break
case '#movie':
this.comName = 'Moive'
break
case '#about':
this.comName = 'About'
break
}
}
}
vue-router
vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。
vue-router 安装和配置的步骤
① 安装 vue-router 包
- npm i vue-router@3.5.2 -S
② 创建路由模块
-
在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:
import Vue from 'Vue' import VueRouter from 'vue-router' // 调用 Vue.use() 函数,把 VueRouter 安装为 Vue 插件 Vue.use(VueRouter) const router = new VueRouter() export default router
③ 导入并挂载路由模块
-
在 src/main.js 入口文件中,导入并挂载路由模块。
import Vue from 'vue' import App from './App.vue' // 1 导入路由模块 // 在进行模块化导入时,如果给定的是文件夹,则默认导入这个文件夹,名字叫做 index.js 的文件 import router from '@/router' new Vue({ render:h => h(App), // 2 挂载路由模块 router:router }).$mount('#app')
④ 声明路由链接和占位符
- 在 src/App.vue 组件中,使用 vue-router 提供的
<router-link>和<router-view>声明路由链接和占位符:
<template>
<div class='app-container'>
<h1> App 组件 </h1>
// 定义路由链接
<router-link to="/home">首页</router-link>
<router-link to="/movie">电影</router-link>
<router-link to="/about">关于</router-link>
<hr>
// 定义路由的占位符
<router-view></router-view>
</template>
声明路由的匹配规则
-
在 src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则。
// 导入需要使用路由切换展示的组件 import Home from '@/components/Home.vue' import Home from '@/components/Movie.vue' import Home from '@/components/About.vue' // 创建路由的实例对象 const router = new VueRouter({ routers:[ // 在 routes 数组中,声明路由的匹配规则 // 路由规则 // path 表示要匹配的 hash 地址;component表示要展示的路由组件 {path:'/home',component:Home}, {path:'/movie',component:Movie}, {path:'/about',component:About} ] })
vue-router 的常见用法
路由重定向
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
const router = new VueRouter({
routers:[
// 当用户访问 / 时,通过 redirect 属性跳转到 /home 对应的路由规则
{path:'/',redirect:'/home'}
{path:'/home',component:Home},
{path:'/movie',component:Movie},
{path:'/about',component:About}
]
})
嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由。
- 点击父级路由链接显示模板内容
1 声明子路由链接和子路由占位符
-
在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符。
<template> <div class="about-container"> <h3>About 组件</h3> // 在关于页面中,声明两个子路由链接 <router-link to="/about/tab1">tab1</router-link> <router-link to="/about/tab2">tab2</router-link> <hr/> <router-view></router-view> </template>
2 通过 children 属性声明子路由规则
在 src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则
import Tab1 from '@/component/tabs/Tab1.vue'
import Tab1 from '@/component/tabs/Tab2.vue'
const router = new VueRouter({
routers:[
{ // about 页面的路由规则(父级路由规则)
path:'/about',
component:About,
children:[ // 通过children 属性,嵌套声明子级路由规则
{path:'tab1',component:Tab1}, // 访问 /about/tab1
{path:'tab2',component:Tab2}
]
}
]
})
动态路由匹配
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。
< path:'/movie/:id',component:Movie
$route.params 路由参数对象
在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值。
在 <template>中使用 <h3>{{this.$route.params.id}}
在 <script> 中写 export default{ name:'Movie' }
路由的query参数
传递参数
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="`/about/message/detail?id=${item.id}&title=${item.title}`">{{ item.title }}</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
接收查询参数:
$route.query.id
$route.query.title
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。
< path:'/movie/:id',component:Movie, props:true>
在 Movie 组件中写 props:['id']
直接使用 props 中接收的路由参数
<h3>{{ id }}</h3>
或者 $route.params.id
注意:
1 '/' 后面的参数叫做 路径参数
- 使用
$route.params.id访问 路径参数
2 '?' 后面叫查询参数
- 使用
$route.query.z访问 查询参数
3 在 this.$route 中 path 只是路径的一部分, fullPath 是完整的地址
4 params 与 query 的区别
- params传参:是在内存中传参,刷新会丢失
- query传参:是在地址栏传参,刷新还在
声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。
- 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
vue-router 中的 编程式导航 API
① this.$router.push('hash 地址')
-
跳转到指定 hash 地址,并增加一条历史记录,展示对应的组件页面。
-
export default{ mehtods:{ getoMovie(){ this.$router.push('/movie/1') } } }
② this.$router.replace('hash 地址')
- 跳转到指定的 hash 地址,不会增加历史记录并替换掉当前的历史记录
③ this.$router.go(数值 n)
-
实现导航历史前进、后退
-
export default{ props:['id'], methods:{ goBack(){ this.$router.go(-1) } } } -
如果后退的层数超过上限,则原地不动
-
$router.go 的简化用法
- ① $router.back()
- ② $router.forward()
导航守卫
导航守卫可以控制路由的访问权限。
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制:
const router =new Vue({...})
router.beforeEach(fn)
守卫方法的 3 个形参
const router =new Vue({...})
router.beforeEach((to,from,next)=>{
// to 是将要访问的路由的信息对象
// from 是将要离开的路由的信息对象
// next 是一个函数,调用 next() 表示放行,允许这次路由导航
})
next 函数的 3 种调用方式
控制后台主页的访问权限
router.beforeEach((to,from,next))=>{
if(to.path === '/main'){
const token = localStorage.getItem('token')
if(token){
next() // 访问的是后台主页,且有 token 的值
}else{
next('/login')
}
}else{
next() // 访问的不是后台主页,直接发行
}
}
路由2
- 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
- 前端路由:key是路径,value是组件。
1.基本使用
-
安装vue-router,命令:
npm i vue-router -
应用插件:
Vue.use(VueRouter) -
编写router配置项:
// router/index.js //引入VueRouter import VueRouter from 'vue-router' //引入Luyou 组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) //暴露router export default router -
实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link> -
指定展示位置
<router-view></router-view>
2.几个注意点
- 路由组件通常存放在
pages文件夹,一般组件通常存放在components文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route属性,里面存储着自己的路由信息。 - 整个应用只有一个router,可以通过组件的
$router属性获取到。
3.多级路由(多级路由)
-
配置路由规则,使用children配置项:
// router/index.js routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ //通过children配置子级路由 { path:'news', //此处一定不要写:/news component:News }, { path:'message',//此处一定不要写:/message component:Message } ] } ] -
跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
4.路由的query参数
-
传递参数
// Message.vue <!-- 跳转并携带query参数,to的字符串写法 --> <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> // 页面中不需要 <!-- 跳转并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link> -
接收参数:
// Detail.vue $route.query.id $route.query.title
5.命名路由
-
作用:可以简化路由的跳转。
-
如何使用
-
给路由命名:
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] } -
简化跳转:
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
-
6.路由的params参数
-
配置路由,声明接收params参数
-
:占位符 -
页面必需的参数,使用内嵌参数 params
// router/index.js { path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] } -
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="`/home/message/detail/${{item.id}}/${{item.title}}`">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', //params to的对象写法不能写 path name是路由的名字 params:{ id:666, title:'你好' } }" >跳转</router-link>特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
-
接收参数:
// Detail.vue $route.params.id $route.params.title
7.路由的props配置
作用:让路由组件更方便的收到参数
// router/index.js
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的`key-value`的组合最终都会通过props传给Detail组件(传递死数据,不常用)
// props:{a:900,b:10} // Detail.vue -> props:['a','b']
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有`params`参数通过props传给Detail组件(query参数收不到
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件 (推荐)
props({ query }){ // $route
return {
id:route.query.id,
title:route.query.title
}
}
// 连续解构赋值
props({ params: { id, title } }) {
return { id: id, title: title }
}
}
8.<router-link>的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push - 如何开启
replace模式:<router-link replace>News</router-link>
9.编程式路由导航
-
作用:不借助
<router-link>实现路由跳转,让路由跳转更加灵活 -
具体编码:
//$router的两个API this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', // 可以任意指定name跳转 params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退 $route: 路由规则 $router: 路由器
10.缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive>
11.两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated路由组件被激活时触发。deactivated路由组件失活时触发。
12.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 // 在需要鉴权的路由规则下: meta:{ isAuth:true } router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 // 作用:点击改变页面 title // routes: meta:{title:'about'} router.afterEach((to,from)=>{ console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } }) -
独享守卫:
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() } } -
组件内守卫:
//进入守卫:通过`路由规则`,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫:通过`路由规则`,离开该组件时被调用 beforeRouteLeave (to, from, next) { next() }
13.路由器的两种工作模式
1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
3. hash模式:
1. 地址中永远带着#号,不美观 。
2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3. 兼容性较好。
4. history模式:
1. 地址干净,美观 。
2. 兼容性和hash模式相比略差。
3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。// nginx
// router:
mode:'hash/history'
路由404
{path:'*',component:NotFound}
声明式导航 - 两个类名
router-link会自动给当前导航添加两个类名
router-link-active: 激活的导航链接 模糊匹配
to="/my" 可以匹配 /my /my/a /my/b ....
router-link-exact-active: 激活的导航链接 精确匹配
to="/my" 仅可以匹配 /my
Vue封装的过度与动画
-
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
-
写法:
-
准备好样式:
- 元素进入的样式:
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
- 元素离开的样式:
- v-leave:离开的起点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
- v 即
<transition>里的name.hello-enter-active
- 元素进入的样式:
-
使用
<transition>包裹要过度的元素,并配置name属性:<transition name="hello"> <h1 v-show="isShow">你好啊!</h1> </transition> -
备注:若有多个元素需要过度,则需要使用:
<transition-group>,且每个元素都要指定key值。
-
vue脚手架配置代理
方法一
在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
- 8080 请求 5000
方法二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
:star:Vue3
ES6模块化与异步编程高级用法
ES6模块化
ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
定义:
-
每个 js 文件都是一个独立的模块
-
导入其它模块成员使用 import 关键字
-
向外共享模块成员使用 export 关键字
基于 node.js 学习 ES6需配置:
- v14.15.1以上版本的 node.js
- 在 package.json 的根节点中添加 "type":"module"
语法:
① 默认导出与默认导入
- 默认导出:
- 语法:
export default{ 值类型成员,模块私有方法... }(:heavy_exclamation_mark:export default只能使用一次)
- 语法:
- 默认导入:
import 接收名称 from '模块标识符'
② 按需导出与按需导入
-
按需导入
import { s1,say } from '模块标识符'
-
按需导出
export let s1 = 'ben';export function...
-
注意:
① 每个模块中可以使用多次按需导出
② 按需导入的成员名称必须和按需导出的名称保持一致
③ 按需导入时,可以使用 as 关键字进行重命名
④ 按需导入可以和默认导入一起使用
③ 直接导入并执行模块中的代码
只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。
import '模块标识符'
:star:Promise
回调地狱
多层回调函数的相互嵌套,就形成了回调地狱。
缺点:
- 代码耦合性太强,牵一发而动全身,难以维护
- 大量冗余的代码相互嵌套,代码的可读性变差
解决回调地狱--promise
基本概念
Promise 是一个构造函数
const p = new Promise()// new实例 代表一个异步操作
Promise.prototype 上包含一个 .then() 方法
- 实例可以通过 原型链 的方式访问到 .then() 方法,p.then()
.then() 方法用来预先指定成功和失败的回调函数
- p.then(成功的回调函数【必选】,失败的回调函数【可选】)
- p.then(result => { }, error => { })
基于回调函数按顺序读取文件内容
基于 then-fs 读取文件内容
1 import thenFs from 'then-fs'
2 thenFs.readFile('./1.txt','utf8').then(r1 => {log r1},err1 => {log err1.message})(重复写)
注意:失败回调可选,无法保证文件读取顺序
基于 Promise 按顺序读取文件的内容
Promise 支持链式调用,从而来解决回调地狱的问题。
import thenFs from 'then-fs'
thenFs.readFile('./1.txt','utf8').then((r1) => {
return thenFs.readFile('./2.txt','utf8')
}).then((r2) => {
return thenFs.readFile('./3.txt','utf8')
}).then((r3) => {
console.log(r3)
}).catch(err => {
console.log(err.message)
})
通过 .catch 捕获错误
(...).catch(err => {
console.log(err.message)
})
如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前
thenFs.readFile('./1.txt','utf8').catch(err => { console.log(err.message) }).then...
- 捕获前面发生的错误,并输出错误的信息
- 由于错误已被及时处理,不影响后面 .then 的正常进行
Promise.all()方法
Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)。
Promise.race() 方法
Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。
基于 Promise 封装读文件的方法
function getFile(fpath){
return new Promise(function(resolve,reject){
fs.readFile(fpath,'utf8',(err,dataStr)=>{
if(err) return reject(err)
resolve(dataStr)
})
})
}
getFile('./1.txt').then(resolve,reject)
async/await
async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作。
import thenFs from 'then-fs'
async function getAllFile(){
const r1 = await thenFs.readFile('./1.txt')
const r2 = await thenFs.readFile('./2.txt')
const r3 = await thenFs.readFile('./3.txt')
}
getAllFile()
:heavy_exclamation_mark:注意
① 如果在 function 中使用了 await,则 function 必须被 async 修饰
② 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
:star:EventLoop
JavaScript 是单线程的语言
也就是说,同一时间只能做一件事情
问题:
- 如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。
同步任务和异步任务
JavaScript 把待执行的任务分为了两类,防止某个耗时任务导致程序假死
① 同步任务(synchronous)
-
又叫做非耗时任务,指的是在主线程上排队执行的那些任务
-
只有前一个任务执行完毕,才能执行后一个任务
② 异步任务(asynchronous)
-
又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
-
当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数
同步任务和异步任务的执行过程
EventLoop 的基本概念
JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这 个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)。
宏任务和微任务
异步任务的两种类别
js 主线程先执行完 宏任务 之后再判断有无微任务,没有再执行下一个 宏任务
① 宏任务(macrotask)
-
异步 Ajax 请求、
-
setTimeout、setInterval、
-
文件操作
-
其它宏任务
② 微任务(microtask)
-
Promise.then、.catch 和 .finally
-
process.nextTick
-
其它微任务
宏任务和微任务的执行顺序
前端工程化
组件化、模块化、规范化、自动化
- 前端工程化的解决方案
- webpack(打包构建)
- parcel
webpack
- 解决 浏览器不兼容 ES6 语法
Vue的基本使用
Vue全家桶
- vue(核心库)
- vue-router(路由方案)
- vuex(状态管理方案)
- vue 组件库(快速搭建页面 UI 效果的方案)
工具:
vue-cli、vite、vue-devtools、vetur
graph LR;
构建用户界面-->指令;
构建用户界面-->数据驱动视图;
构建用户界面-->事件绑定;
前端框架-->构建用户界面的一整套解决方案;
前端框架-->vue全家桶;
前端框架-->配套工具;
Vue 特性
-
数据驱动视图
-
双向数据绑定
Vue3 新增功能:
- 组合式API、多根节点组件、更好的TypeScript支持
废弃的旧功能:
-
过滤器、不再支持 off 和 $once 实例方法等
过滤器(Filters)常用于文本的格式化。
可以用在:
-
插值表达式
{{msg | capitalize}} -
v-bind 属性绑定
:id="rawId | formatId"
组件基础
单页面应用程序(SPA)
一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成。
SPA 特点:
-
将所有的功能局限于一个 web 页面中,仅在该 web 页面初始化时加载相应的资源( HTML、JavaScript 和 CSS)。
-
一旦页面加载完成了,SPA 不会因为用户的操作而进行页面的重新加载或跳转。而是利用 JavaScript 动态地变换 HTML 的内容,从而实现页面与用户的交互。
SPA 优点:
- 1 良好的交互体验
- 单页应用的内容的改变不需要重新加载整个页面
- 获取数据也是通过 Ajax 异步获取
- 没有页面之间的跳转,不会出现“白屏现象”
- 2 良好的前后端工作分离模式
- 后端专注于提供 API 接口,更易实现 API 接口的复用
- 前端专注于页面的渲染,更利于前端工程化的发展
- 3 减轻服务器的压力
- 服务器只提供数据,不负责页面的合成与逻辑的处理,吞吐能力会提高几倍
SPA 缺点:
- 1 首屏加载慢
- 路由懒加载
- 代码压缩
- CDN 加速
- 网络传输压缩
- 2 不利于 SEO
- SSR 服务器端渲染
两种快速创建工程化的 SPA 项目的方式:
| vite | vue-cli | |
|---|---|---|
| 支持版本 | 仅支持 vue3 | 支持 3.x 和2.x |
| 基于 webpack? | no | yes |
| 运行速度 | 快 | 较慢 |
| 功能完整度 | 小而巧(逐渐完善 | 大而全 |
| 建议在企业级开发中使用? | 目前不建议 | 建议 |
组件化开发思想
根据封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护。
好处:
-
提高了前端代码的复用性和灵活性
-
提升了开发效率和后期的可维护性
vue 是一个完全支持组件化开发的框架。vue 中规定组件的后缀名是 .vue。
vue 组件组成结构
- template -> 组件的模板结构(必选)
- script -> 组件的 JavaScript 行为(可选)
<script> export default { name,data数据、methods方法...} </script>- name 节点为当前组件定义一个名称(建议每个单词首字母大写
name:'MyApp'- 在注册组件时:app.component(Swiper.name,Swiper)
- data必须是一个函数,不能直接指向一个数据对象
data(){return {}}
methods:{ 事件处理函数 }components:{ 'my-search':Search,}
- style -> 组件的样式(可选)
<style lang="css"> h1{...}</style>lang="css"(默认)还有 less(npm i less -D)、scss
在 template 中定义根节点:
-
在 vue2 中,