Vue2、Vue3知识总结---完整版(下)✨

686 阅读20分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 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

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写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
    
  4. 实现切换(active-class可配置高亮样式)

    <router-link active-class="active" to="/about">About</router-link>
    
  5. 指定展示位置

    <router-view></router-view>
    

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(多级路由)

  1. 配置路由规则,使用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
    			}
    		]
    	}
    ]
    
  2. 跳转(要写完整路径):

    <router-link to="/home/news">News</router-link>
    

4.路由的query参数

  1. 传递参数

    // 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>
    
  2. 接收参数:

    // Detail.vue
    $route.query.id
    $route.query.title
    

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      {
      	path:'/demo',
      	component:Demo,
      	children:[
      		{
      			path:'test',
      			component:Test,
      			children:[
      				{
                            name:'hello' //给路由命名
      					path:'welcome',
      					component:Hello,
      				}
      			]
      		}
      	]
      }
      
    2. 简化跳转:

      <!--简化前,需要写完整的路径 -->
      <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参数

  1. 配置路由,声明接收params参数

  2. : 占位符

  3. 页面必需的参数,使用内嵌参数 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
    				}
    			]
    		}
    	]
    }
    
  4. 传递参数

    <!-- 跳转并携带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配置!

  5. 接收参数:

    // 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属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$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.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    <keep-alive include="News"> 
        <router-view></router-view>
    </keep-alive>
    

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

12.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    //全局前置守卫:初始化时执行、每次路由切换前执行
    
    // 在需要鉴权的路由规则下:
    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'
    	}
    })
    
  4. 独享守卫:

    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()
    	}
    }
    
  5. 组件内守卫:

    //进入守卫:通过`路由规则`,进入该组件时被调用
    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封装的过度与动画

  1. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

  2. 写法:

    1. 准备好样式:

      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
      • v 即 <transition>里的name
        • .hello-enter-active
    2. 使用<transition>包裹要过度的元素,并配置name属性:

      <transition name="hello">
      	<h1 v-show="isShow">你好啊!</h1>
      </transition>
      
    3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

vue脚手架配置代理

方法一

​ 在vue.config.js中添加如下配置:

devServer:{
  proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
  4. 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
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

:star:Vue3

vue3基础入门参考文章!必看

ES6模块化与异步编程高级用法

ES6模块化

ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。

定义:
  • 每个 js 文件都是一个独立的模块

  • 导入其它模块成员使用 import 关键字

  • 向外共享模块成员使用 export 关键字

基于 node.js 学习 ES6需配置:

  1. v14.15.1以上版本的 node.js
  2. 在 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支持

废弃的旧功能:

  • 过滤器、不再支持 onon 、off 和 $once 实例方法等

  • 迁移指南

过滤器(已剔除)

过滤器(Filters)常用于文本的格式化。

可以用在:

  1. 插值表达式

    {{msg | capitalize}}

  2. 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 项目的方式:
vitevue-cli
支持版本仅支持 vue3支持 3.x 和2.x
基于 webpack?noyes
运行速度较慢
功能完整度小而巧(逐渐完善大而全
建议在企业级开发中使用?目前不建议建议
组件化开发思想

根据封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护。

好处:

  • 提高了前端代码的复用性和灵活性

  • 提升了开发效率和后期的可维护性

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 中,