Vue2(三)

发布于:2025-02-11 ⋅ 阅读:(76) ⋅ 点赞:(0)

1.指令

1.1自定义指令

v-html、v-if、v-bind、v-on... 这都是Vue给咱们内置的一些指令,可以直接使用。Vue也支持让开发者,自定义自己的指令。

进行指令注册

//在main.js中全局注册。
Vue.directive('指令名', {
  "inserted" (el) { //inserted会在指令所在的元素,被插入到页面时触发。el就是指令所绑定的元素
    el.focus()  // 可以对el标签,扩展额外功能。这里代表获取焦点。
  }
})
//在组件中局部注册,只能在当前组件范围内使用。
directives: {
  "指令名": {
    inserted () {
      el.focus()  //可以对el标签,扩展额外功能。这里代表获取焦点。
    }
  }
}

使用指令

<input type="text"  v-指令名/>  

1.2.自定义指令的值

自定义指令时,就可以通过“等号”的形式为指令绑定具体的参数值了

<div v-color="color">我是内容</div>

通过 binding.value 可以拿到指令值,指令值修改会触发 update 函数 

directives: {
  color: {
    inserted (el, binding) {
      el.style.color = binding.value
    },
    update (el, binding) { //指令中绑定的参数值发生了改变时。update函数会自动执行
      el.style.color = binding.value
    }
  }
}

1.3.自定义v-loading指令

实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态,用户体验不好。这种情况下,我们就可以封装一个 v-loading 指令,实现加载中的效果。

本质 loading 效果就是一个蒙层,盖在了盒子上。数据请求中,开启loading状态,添加蒙层;数据请求完毕,关闭loading状态,移除蒙层。下面是v-loading指令在组件中的具体实现以及应用。

<template>
  <div class="main">
    <div class="box" v-loading="isLoading">
      <ul>
        <li v-for="item in list" :key="item.id" class="news">
          <div class="left">
            <div class="title">{
  
  { item.title }}</div>
            <div class="info">
              <span>{
  
  { item.source }}</span>
              <span>{
  
  { item.time }}</span>
            </div>
          </div>

          <div class="right">
            <img :src="item.img" alt="">
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
// 安装axios =>  yarn add axios
import axios from 'axios'

// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
  data () {
    return {
      list: [],
      isLoading: true,
      isLoading2: true
    }
  },
  async created () {
    // 1. 发送请求获取数据
    const res = await axios.get('http://hmajax.itheima.net/api/news')
    
    setTimeout(() => {
      // 2. 更新到 list 中,用于页面渲染 v-for
      this.list = res.data.data
      this.isLoading = false
    }, 2000)
  },
  directives: {
    loading: {
      inserted (el, binding) {
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      },
      update (el, binding) {
        binding.value ? el.classList.add('loading') : el.classList.remove('loading')
      }
    }
  }
}
</script>

<style>
.loading:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #fff url('./loading.gif') no-repeat center;
}

.box {
  width: 800px;
  min-height: 500px;
  border: 3px solid orange;
  border-radius: 5px;
  position: relative;
}
.news {
  display: flex;
  height: 120px;
  width: 600px;
  margin: 0 auto;
  padding: 20px 0;
  cursor: pointer;
}
.news .left {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-right: 10px;
}
.news .left .title {
  font-size: 20px;
}
.news .left .info {
  color: #999999;
}
.news .left .info span {
  margin-right: 20px;
}
.news .right {
  width: 160px;
  height: 120px;
}
.news .right img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>

2.插槽

插槽可以让通用组件内部的一些 结构 支持 自定义。我们在使用通用组件的时候,如果不想写死组件内的一些结构,这个时候就可以使用插槽。

给插槽传入内容时,可以传入纯文本、html标签、组件。

2.1 默认插槽

公共组件内只有一个地方需要传入外部内容进行定制时,可以使用默认插槽。默认插槽的使用步骤如下:

在通用组件内使用<slot></slot>占位。

<template>
  <div class="dialog">
    <div class="dialog-header">
      <h3>友情提示</h3>
      <span class="close">✖️</span>
    </div>

    <div class="dialog-content">
      <slot>您确定要进行删除操作吗?</slot>
    </div>
    <div class="dialog-footer">
      <button>取消</button>
      <button>确认</button>
    </div>
  </div>
</template>

在父组件中,给公共组件传入插槽内容。

<template>
  <div>
    <MyDialog>你确定要退出本系统吗?</MyDialog>
  </div>
</template>

2.2 具名插槽

一个组件内有多处结构,需要外部传入标签,进行定制的话,默认插槽就没用了。这个时候就需要用到具名插槽。比如下面的弹框中有三处不同,但是默认插槽只能定制一个位置。

//公共组件中多个slot使用name属性区分名字 
<div class="dialog-header">
  <slot name="head"></slot>
</div>
<div class="dialog-content">
  <slot></slot>
</div>
<div class="dialog-footer">
  <slot name="footer"></slot>
</div>
//父组件的template中配合v-slot:名字来分发对应标签。默认插槽和具名插槽同时存在时,
//无需为默认插槽指定v-slot,内容会自动被传递过去。
<MyDialog>
  <template v-slot:head>  //v-slot可以简写为#
    大标题
  </template>
  <template> 
    大标题
  </template>
  <template v-slot:footer>
    <button>按钮</button>
  </template>
</MyDialog>

2.3 插槽的默认内容

通过插槽完成了内容的定制,传什么显示什么,但是如果不传,则是空白。这种情况下,可以给插槽设置默认显示内容。

在 <slot> 标签内,放置的内容是默认显示内容。如果父组件传过来内容,则采用父组件的内容;如果父组件没有传过来内容,则采用默认内容。

2.4 作用域插槽

作用域插槽是插槽传参的一个语法,并不属于插槽的分类。作用域插槽就是在定义 slot 插槽的同时,可以给插槽上绑定数据,将来使用组件时可以用。使用步骤如下;

1.给 slot 标签, 以添加属性的方式传值

<slot :id="item.id" msg="测试文本"></slot>

2.所有添加的属性,都会被收集到一个对象中

{ id: 3, msg: '测试文本' }

3.提供给默认插槽的内容需要用 template 标签包裹。然后在 template 标签上通过 #插槽名= "obj" 接收,默认插槽名为 default。这里的<template>标签是为了接收参数而额外加的。

<MyTable :list="list">
  <template #default="obj"> //如果是具名插槽。则default替换为插槽的名字。
    <button @click="del(obj.id)">删除</button>
  </template>
</MyTable>

4.路由

4.1.VueRouter

一般来说,通过Vue开发出的网站就属于单页应用程序。单页应用程序(SPA)是指所有的功能都在一个html页面上实现。它和多页应用程序的主要区别如下。

Vue中的VueRouter是一个路由插件,它明确了路径和组件的对应关系,能够实现单页应用程序的按需更新。

4.2.VueRouter的使用

1. 下载 VueRouter 模块到当前工程

yarn add vue-router@3.6.5

2. main.js中引入VueRouter

import VueRouter from 'vue-router'

3. main.js中进行安装注册

Vue.use(VueRouter)

4. main.js中创建路由对象

const router = new VueRouter()

5. main.js中将路由对象注入到 new Vue 实例中,建立关联

new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

当我们配置完以上 5 步之后 就可以看到浏览器地址栏中的路由变成了 /#/ 的形式。表示项目的路由已经被 Vue-Router 管理了。

6. 创建需要的组件,并在main.js的路由对象中配置路由规则

一般创建好的组件是放在views目录,这些组件代表配置路由规则时使用的组件。

import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'

const router = new VueRouter({
  routes: [
    { path: '/login', component: Login },
    { path: '/my', component: My},
    { path: '/friend', component: Friend },
  ]
})

7. 定义导航和路由出口

路由出口就说路径匹配上的组件显示的位置。

<template>
  <div>
    <!-- 导航栏,用来切换路径。路径切换成功之后,<router-view>处就会展示对应的组件 -->
    <div class="footer_wrap">
      <a href="#/find">发现音乐</a>
      <a href="#/my">我的音乐</a>
      <a href="#/friend">朋友</a>
    </div>
    <div class="top">
      <!-- 路由出口。放在导航栏下面就代表在它的下面展示 -->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

4.3.多级路由

在 Vue 中,路由是有多个级别的,可分为以下几级:

一级路由:一级路由通常代表应用的主要页面。但凡是单个页面,独立展示的,都是一级路由。例如,一个网站的主页、登录页、用户页等。

const routes = [
  {
    path: '/',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
];

二级路由:二级路由通常用于在一个页面内嵌套显示其他页面的内容。可以通过在一个路由的 component 中使用 children 属性来定义子路由。

const routes = [
  {
    path: '/',
    component: Home,
    children: [
      {
        path: 'profile',
        component: Profile
      },
      {
        path: 'settings',
        component: Settings
      }
    ]
  }
];

三级及更深层次的路由:Vue 路由本身支持无限层级的嵌套路由,所以可以继续在子路由下嵌套子路由,从而实现三级或更深的路由。

const routes = [
  {
    path: '/',
    component: Home,
    children: [
      {
        path: 'profile',
        component: Profile,
        children: [
          {
            path: 'edit',
            component: ProfileEdit
          }
        ]
      }
    ]
  }
];

4.4.路由配置的抽离

上述的路由配置都放在了main.js中,这样在配置过多时,非常不利于维护,可以将路由相关的配置单独抽取出来。具体步骤如下:

1.在项目的src目录下新建一个router目录,在目录中创建一个js文件放路由相关的配置

import Vue from 'vue'
import VueRouter from 'vue-router'

import Find from '../views/Find'
import My from '../views/My'
import Friend from '../views/Friend'

Vue.use(VueRouter) // VueRouter插件初始化

const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})

export default router

2.在main.js文件中引入VueRouter对象即可

import Vue from 'vue'
import App from './App.vue'
import router from './router/index'

Vue.config.productionTip = false

Vue.use(VueRouter) // VueRouter插件初始化

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

4.5.声明式导航

也叫导航链接。如果使用 a 标签进行跳转的话,需要给当前跳转的导航加样式,同时要移除上一个a标签的样式,太麻烦。vue-router 提供了一个全局组件 router-link,可以用来取代 a 标签。router-link 本质就是对 a 标签的增强。

增强的内容如下:

  • 能跳转,配置 to 属性指定路径,路径中无需带上 #。

  • 选中哪个<router-link>标签,这个标签就会被添加二个类名(router-link-exact-active和router-link-active),可以根据这二个类名用CSS为其设置样式。

  <div>
    <div class="footer_wrap">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/friend">朋友</router-link>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>

二个类名的区别

router-link-active(模糊匹配):若选中的路径是 /find,则除了 /find 之外,以 /find 开头的<router-link>标签也都会被添加上该类名。比如 /find/one、 /find/two。

router-link-exact-active(模糊匹配):若选中的路径是 /find、则只有 /find 对应的 <router-link> 标签才会被添加上该类名。

修改这二个类名

如果嫌这二个类名太长,可以在router对象中进行配置。

const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
  // router-link标签中的类名修改
  router-link-exact-active: 'active', //配置模糊匹配的类名
  router-link-active: 'exact-active' //配置精确匹配的类名,前面要加上exact-
})

4.6.路由传参

我们可以通过两种方式,在跳转的时候把所需要的参数传到其它页面中。

1.查询参数传参(适合传递多个参数)

传递参数:

<router-link to="/path?参数名=值"></router-link>

接收参数:

//在 html 标签中
$router.query.参数名
//在 js 代码中
this.$router.query.参数名

2.动态路由传参(适合传递单个参数)

在router对象中配置路径参数

const router = new VueRouter({
  routes: [
    ...,
    { 
      path: '/search/:words', //":words"就是配置的内容
      component: Search 
    }
  ]
})

传递参数:

<router-link to="to="/path/参数值"></router-link>

接收参数:

//在html标签中
$route.params.参数名
//在js代码中
this.$route.params.参数名

动态路由存在的一个小问题

配了路由 path:"/search/:words" 为什么按下面步骤操作,会未匹配到组件,显示空白?

/search/:words 表示,必须要传参数,如果不传,是无法匹配到组件的。如果不传参数也希望匹配,加个可选符 "?"就好了。

const router = new VueRouter({
  routes: [
 	...
    { path: '/search/:words?', component: Search }
  ]
})

4.7.路由重定向

路由重定向指的是当访问某个特定的路由时,自动重定向到另一个路由。

当我们访问一个网站时, url 默认路径是" / "," / "这个路径不会匹配到任何组件,页面就会出现空白。这种情况可以利用路由重定向来解决,在匹配到路径" / "后,强制跳转到主页。.

const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home'},
 	 ...
  ]
})

4.8.路由跳转

path路径跳转:简易方便

//在标签里进行跳转
<button @click="$router.push('/路径?参数名1=参数值1&参数2=参数值2')">Go to Home</button>
//在js代码里进行跳转
//第一种
//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2') 
//完整写法
this.$router.push({ 
  path: '/路径',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

//跳转后接收参数的方式
$route.query.参数名


//第二种
/简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
  path: '/路径/参数值'
})

//跳转后接收参数的方式
$route.params.参数值

name命名路由跳转:适合 path 路径长的场景。

//提前在路由对象中配置
const router = new VueRouter({
  routes: [
    { 
      name: 'search',
      path: '/search/:words', 
      component: Search 
    }
  ]
})
//在标签里进行跳转
<button @click="$router.push('/路径?参数名1=参数值1&参数2=参数值2')">Go to Home</button>
//在js代码里进行跳转
//第一种
//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2') 
//完整写法
this.$router.push({
  name: '路由名字', //该方式需要定义name属性
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

//跳转后接收参数的方式
$route.query.参数名

//第二种
//简单写法
this.$router.push('/路径/参数值') 
//完整写法
this.$router.push({
  name: '路由名字',
  params: {
    参数名: '参数值', //这里的参数名要和路由配置中的路径参数名对应
  }
})

//跳转后接收参数的方式
$route.param.参数名

4.9.导航守卫

我们开发的项目中,有些页面是不需要登录就可以访问的;有些页面登录后才可以访问,如果用户未登录,就需要提示用户并跳转到登录页。这个时候就可以用到 Vue Router 的导航守卫。

找到路由对象所在的 js 文件,在文件中调用路由对象的全局前置守卫方法 beforeEach。这样,在浏览器中每访问一个URL,都会经过 beforeEach 的检查。

const authUrl = ['/pay', '/myorder']
// 1. to   往哪里去, 到哪去的路由信息对象  
// 2. from 从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
//    如果next()调用,就是放行
//    next(路径) 拦截到某个路径页面
router.beforeEach((to, from, next) => {
  const token = store.getters.token
  if (!authUrl.includes(to.path)) {
    next()
    return
  }

  if (token) {
    next()
  } else {
    next('/login')
  }
})

4.10 路由解决的一些小问题

404的解决:当路径找不到匹配时,可以给个提示页面,可以通过如下所示的路由规则跳转到提示页面。该路由规则一般配置在路由规则的最后面。

import NotFind from '@/views/NotFind'

const router = new VueRouter({
  routes: [
    ...
    { path: '*', component: NotFind } //*代表匹配所有,NotFind是404后要跳转的页面(.vue文件)
  ]
})

路径中#的去除:路由的路径中有"#"看起来不自然,我们可以对路由模式进行切换。

const router = new VueRouter({
    mode:'histroy', //默认是hash
    routes:[]
})

5.缓存组件

从面经列表点到详情页,再点返回,发现数据重新加载了,而我们希望回到原来的位置。这是因为路由跳转后,原来所看到的组件就被销毁了,重新返回后组件会被重新创建,所以数据也会被重新加载。解决方案:利用keep-alive把原来的组件给缓存下来。

keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。同时,减少加载时间及性能消耗,提高用户体验性。

<template>
  <div class="h5-wrapper">
    <keep-alive :include="['LayoutPage']">  //只有LayoutPage.vue组件会被缓存
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

keep-alive标签中可以配置三个属性

  • include : 组件名数组,只有匹配的组件会被缓存

  • exclude : 组件名数组,任何匹配的组件都不会被缓存

  • max : 最多可以缓存多少组件实例

keep-alive额外的两个生命周期钩子

组件缓存后就不会执行组件的 created, mounted, destroyed 等钩子了,所以其提供了actived 和deactived钩子,keep-alive使用的时候这二个钩子会自动触发,帮我们实现业务需求。

  • activated:当回到被缓存的组件时触发

  • deactivated:当离开被缓存的组件时触发

6.ESlint

ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则,你和你的团队可以自行约定一套规则。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则。

1.JavaScript Standard Style 规范说明

建议把:JavaScript Standard Style 看一遍,然后在写的时候, 遇到错误就查询解决。下面是这份规则中的一小部分:

  • 字符串使用单引号 – 需要转义的地方除外

  • 无分号没什么不好。不骗你!

  • 关键字后加空格 if (condition) { ... }

  • 函数名后加空格 function name (arg) { ... }

  • 坚持使用全等 === 摒弃 == 一但在需要检查 null || undefined 时可以使用 obj == null

  • .....

2.代码规范错误

如果你的代码不符合standard的要求,eslint会跳出来刀子嘴,豆腐心地提示你。下面我们在main.js中随意做一些改动:添加一些空行,空格。

import Vue from 'vue'
import App from './App.vue'

import './styles/index.less'
import router from './router'
Vue.config.productionTip = false

new Vue ( {
  render: h => h(App),
  router
}).$mount('#app')

按下保存代码之后:你将会看在控制台中输出如下错误:

3.手动修正

根据错误提示来一项一项手动修正。

如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码去 ESLint 规则列表中查找其具体含义。打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。

4.自动修正

1.在VSCode中安装ESlint插件

2.在VSCode的设置里进行配置

// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {
    "source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false

网站公告

今日签到

点亮在社区的每一天
去签到