尚硅谷Vue电商项目笔记1-初始化项目搭建整体结构和路由的使用以及编程式导航的bug解决

初始化项目搭建整体结构和路由的使用以及编程式导航的bug解决

教程地址

使用脚手架创建项目

创建项目执行命令:vue create gulishop-client,项目的目录结构:

  1. node_modules:依赖包
  2. public静态资源,不会被webpack处理
  3. src项目源代码
    • assets目录:静态资源
    • components目录:公共组件和非路由组件
    • App:根组件
    • main.js:入口文件
    • .gitignore:git的忽略文件
    • babel.config.js:babel的配置文件
    • package-lock.json:下载包的详细说明
    • package.json:初始化npm包的配置文件

项目启动自动在浏览器中打开

在package.json配置文件中,在启动命令后面添加–open即可:"serve": "vue-cli-service serve --open",

禁用eslint语法检查

项目默认安装了eslint语法检查工具,默认检查严格级别很高,比如定义一个变量,没有使用,那么项目就会报错,所以在开发阶段将此禁用,在src目录下创建vue.config.js配置文件,该配置文件相当于webpack创建项目延伸出来的配置文件,并非webpack的配置文件,但是可以当作webpack的配置文件使用,配置项会插入到webpack的配置中
官网配置参考

module.exports = {
  lintOnSave:false
}

注意:配置文件的修改都需要重启项目

@别名的配置

@代表src路径,但是在webpack中已经配置过了,可以直接使用,但是没有语法提示,所以需要配置,在src目录下创建jsconfig.json配置文件,配置如下内容:

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*":["src/*"]
    }
  },
  // 表示在node_modeules目录和dist目录不能使用配置的@符号
  "exclude": ["node_modules","dist"]
}

注意:如果出现json文件报错,先格式化文档,然后在vocode设置中搜索check js,将启用或禁用JavaScript文件的语义检查勾选

使用git管理项目

基本使用步骤:

  1. 创建本地库,先删除脚手架项目自带的.git文件,使用git init初始化本地仓库
  2. 创建远程仓库
  3. 让本地代码和远程仓库进行关联:git remote add origin https://github.com/yuanlaiwoshicaiji/testguli.git(关联代码在创建仓库之后就有了)
  4. 本地代码改变推向远程库:git add .或者git add -A提交到暂存区,然后提交的本地仓库git commit -m 'first commit',最后推送到远程仓库git push origin master
  5. 远程代码改变拉取到本地库:git pull origin master
  6. 如果先有远程代码,然后才有本地代码,需要在本地执行clone操作(就不需要自己创建本地仓库了)

页面主体架构

功能页面是上中下结构,上下的结构是不变化的(非路由组件),切换页面的时候只有中间区域的内容在变化,页面却没有刷新,也就是说这是一个单页面应用(SPA),采用的ajax请求,中间变化的部分是路由组件的切换:

  1. Header和Footer是固定的所以是非路由组件
  2. Home,Search,Login,Register都是点击才会出现的,是路由组件

使用非路由组件

创建非路由组件Footer和Header,并且在App根组件中使用

  1. 创建组件
  2. 注册组件:使用配置项components:{Header,Footer}注册组件,注意:在注册之前别忘记引入组件否则无法使用
  3. 使用组件:通过组件标签的方式,直接使用<Header></Header>

路由组件和非路由组件的区别

使用的步骤相同:定义,注册,使用

  1. 定义的目录位置不同:非路由组件在components,目录下创建,路由组件在pages目录或者views目录下创建
  2. 注册的方式不同:非路由组件在要使用的组件当中注册,而路由组件是要在路由配置中注册的
  3. 使用的方式不同:非路由组件直接通过标签的方式去使用,路由组件使用声明式导航<router-link>和编程式导航this.$router.push同时配合<router-view>一起使用

注意:非路由组件和路由组件的声明周期不同:路由组件在切换的时候会销毁重建<keep-alive>,而非路由组件不会

路由的具体使用

  1. 定义:直接在pages目录中进行定义
  2. 注册:
    • 安装vue-router:npm i vue-router@3
    • 创建router目录中的index.js文件(路由器文件)
    • 声明并使用插件
    • 暴露路由器对象,并且配置路由(需要提前引入需要使用的路由组件)
    • 入口文件中引入路由器对象,并且在配置项中进行路由器的配置,配置之后,在搜索组件都有this.$routethis.$router属性
  3. 使用:
    • 使用<router-view>标签指定路由组件显示的位置
    • 使用<router-link>标签切换路由或者this.$router.push()或者this.$router.replace()来切换路由
    • 也可以通过在浏览器改变地址,来测试路由的切换
    • 需要在最后配置一个重定向路由,在没有指定任何路由的时候,显示首页

路由器的配置文件:

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import Home from '@/pages/Home'
import Search from '@/pages/Search'
import Login from '@/pages/Login'
import Register from '@/pages/Register'
export default new VueRouter({
  routes:[
    {
      path:'/home',
      component:Home
    },
    {
      path:'/search',
      component:Search
    },
    {
      path:'/login',
      component:Login
    },
    {
      path:'/register',
      component:Register
    },
    {
      path:'/*',
      redirect:'/home'
    },
  ]
})

入口文件中引入暴露的路由器实例对象并且配置:

import Vue from 'vue'
import App from '@/App.vue'
// 引入路由器对象
import router from '@/router'
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router:router, // key和value相同,可以省略,但是没有省略
}).$mount('#app')

指定路由组件显示的位置:

<template>
  <div>
    <Header></Header>
    <!-- 指定路由组件的入口 -->
    <router-view></router-view>
    <Footer></Footer>
  </div>
</template>

将静态页面转换为组件

拆分Header组件和Footer组件:

  1. 因为使用的是less,所以标签要指定语言<style scoped lang="less">
    • scoped表示,该样式文件的作用域被限制在当前文件内,只对当前文件内的元素生效(但是这种说法不准确)
  2. css样式使用的是less,无法解析,所以要安装less和less-loader,并且只在开发环境中使用
    • npm i less -D
    • npm i less-loader@6 -D
  3. 需要图片,在html和css样式中,使用的路径不一样,并且是相对路径,所以要在对应的组件目录内创建images目录来存放图片,没有图片项目也会启动失败
  4. 对于reset.css文件,对于所有的页面都应该生效,在public目录下创建css文件,在index.html中进行引入<link rel="stylesheet" href="<%= BASE_URL %>css/reset.css">

实现路由组件的切换

  1. 将登陆和免费注册的超链接标签,替换为<router-link>标签,使用声明式导航,跳转到搜索页面
  2. 搜索按钮使用编程式导航,跳转到搜索页面
  3. 将logo的点击也跳转到home页面
// 1. 声明式导航跳转
<router-link to="/login">登陆</router-link>
<router-link to="/register" class="register">免费注册</router-link>
// =======================================================================
// 2. 编程式导航跳转
<button @click="toSearch" class="sui-btn btn-xlarge btn-danger" type="button">搜索</button>
// 对应的methods中的方法
toSearch(){
  this.$router.push('/search')
}

路由组件切换Footer组件的显示和隐藏

在显示Login组件和Register组件的时候,Footer组件是不需要显示的,一共有两种方式实现:

  1. 通过路由的path进行判断
  2. 通过路由配置的meta元数据进行判断,该配置项可以配置我们需要的任何数据

通过路由的路径path进行判断:

<div>
  <Header></Header>
  <router-view></router-view>
  <!-- v-show的值需要一个false,才会隐藏,只要在login页面或者register页面,则判断为false,有一个false,&&则总体为false -->
  <!-- 如果使用||进行判断,假设当前为login页面则,false || true,最后为true,即使在login页面也会显示,所以要使用&& -->
  <Footer v-show="$route.path!=='/login' &&  $route.path!=='/register'"></Footer>
</div>

通过路由配置的meta元数据进行判断:

  1. 通过vue开发者工具可以查看如果当前在Home组件中,则$route.meta为一个空对象
  2. 切换到Register后,$route.meta中有了我们配置的isHidden属性
  3. 因为切换路由组件之后,原本的路由组件实例被销毁,可以通过beforeDestroy()声明周期函数验证,那么新的$route对象也会替换旧的$route对象
{
  path:'/register',
  component:Register,
  meta:{isHidden:true} // 在需要隐藏的路由中配置meta配置,标记是否隐藏
},
// 判断逻辑
<!-- 如果需要隐藏则该值为true,true表示显示,所以取反即可 -->
<Footer v-show="!$route.meta.isHidden"></Footer>

路由传参相关

路由匹配的过程

  1. 点击按钮改变路径进行路由跳转this.$router.push('/search/+this.keyword')或者使用<router-link :to="">
  2. 当路径改变,这个路径就回去路由器对象(路由器配置文件)内部的路由数组中的路由对象进行匹配
    • 传递的params参数属于路径的一部分,也就是必须匹配到占位符才算匹配成功,在开发者工具里查看,$route.path包含params参数,该参数,会被路径后面的:xxx占位符收集到,最终这个参数被放到当前这个路由对象的params属性中
    • 传递的query参数不会再匹配的时候占位,但是query参数再匹配的时候也会被收到当前这个路由对象的query属性中
  3. 匹配成功之后显示对应的路由组件
    • 显示路由组件的同时会把刚才匹配成功的路由对象放在这个组件的$route当中
    • 可以理解为:在路由器文件中的每一个路由配置(路由对象)就对应了不同的$route对象(解析到$route之中)

跳转路由的两种方式

  1. 声明式:使用<router-link>标签
  2. 编程式:使用this.$router.push()方法,编程式导航跳转路由比声明式导航更加灵活,因为使用的是方法,可以编写自己的逻辑

路由传参的两种参数类型

  1. params参数:属于路径的一部分,匹配的时候路由的path当中要照顾到这个参数(使用占位符)
  2. query参数:不属于路径的一部分,匹配的时候,路由的path不需要照顾这个参数

无论是params参数还是query参数,最终匹配完成都会解析到当前这个路由对象当中的params和query属性当中,显示路由组件的时候,会把这个路由对象传递给组件当中的this.$route,所以this.$route就可以获取到之前传递的参数

路由路径携带参数的两种写法

  1. 字符串写法
  2. 对象写法

相关面试题

  1. 指定params参数时可以不可用path和params配置的组合(对象写法)
    • 如果传递的参数只有query参数,没有params参数,那么可以不使用name属性(命令路由),直接使用path
    • 一旦参数中有了params参数,就不能使用path,必须使用name
    • 所以对写法传递参数,最好使用name
  2. 如何让params参数可以传递也也可不传递
    • 假设我这里在浏览器路径中直接输入http://localhost:8080/#/search由于没有传递params参数,所以没有匹配的路径会直接跳转到Home组件
    • 在路由配置的path的占位符后面指定?,就能顺利的在不指定params参数的情况下跳转到Search路由组件
  3. 传递的params参数会出现路径丢失问题,如何解决
    • 假设我没有在输入框中输入内容,则在跳转到Search的时候,收集到的是空字符串,路径为http://localhost:8080/#/?keywordUpper=,发现路径部分的Search部分丢失了
    • 首先必须得在params参数可传递可不传递的前提下,当传递的params参数是空字符串的时候,指定传递为undefeind
  4. 路径传参在组件中是否可以使用props属性接收,可以,但是需要在路由对象中进行配置,props属性
    • 如果props的属性的值是为true,则在路由组件中能够通过props属性接收到所有的params属性
    • 如果props的属性值是一个对象,则传递的是路由组件需要的额外的静态数据
    • 如果props的属性值是一个函数,则这个回调函数能够接收到$route为参数,可以手动返回一个对象,同时返回params参数和query参数

测试代码部分

// 1. 路由对象的配置:
{
  name:'search',
  path:'/search/:keyword?',
  component:Search,
  // props:true,  返回布尔值为true,能够通过props传递params参数
  // props:{username:'tomcat'}  返回自定义对象,想传递什么就传递什么
  // 返回一个函数,箭头函数返回的是一个对象,所以用()包裹
  props:(route)=>({keyword:route.params.keyword,keywordUpper:route.query.keywordUpper})
},
// =======================================================================
// 2. 声明式导航字符串和对象形式的参数传递:要使用data中内容,所以绑定v-bind,又因为字符串写法需要的是字符串,所以使用模板字符串,模板字符串中使用变量${}
<!-- 声明式路由导航,字符串形式传递参数 -->
<router-link :to="`/search/${keyword}?keywordUpper=${keyword.toUpperCase()}`">点击跳转到搜索页面</router-link>
<!-- 声明式路由导航,对象形式传递参数,因为传递了params参数,所以不能使用path属性,而要使用name,命令路由的方式 -->
<!-- 并且如果params参数为空字符串,使用undefined -->
<router-link :to="{name:'search',params:{keyword:keyword || undefined},query:{keywordUpper:keyword.toUpperCase()}}">点击跳转到搜索页面</router-link>
// =======================================================================
// 3. 编程式导航字符串和对象形式的参数传递
toSearch() {
  // 字符串写法
  // this.$router.push('/search/'+this.keyword + '?keywordUpper='+ this.keyword.toUpperCase())
  // 对象写法,因为有传递params参数,所以不能使用path,必须使用命名路由
  this.$router.push({
    name: "search",
    params: { keyword: this.keyword || undefined },
    query: { keywordUpper: this.keyword.toUpperCase() },
  });
},
// =======================================================================
// 4. 用于展示的测试部分:
<template>
  <div>
    params:{{$route.params.keyword}}<br/>
    query:{{$route.query.keywordUpper}}<br/>
    propsParams:{{keyword}}<br/>
    propsQuery:{{keywordUpper}}<br/>
    propsTomcat:{{username}}
  </div>
</template>

编程式路由跳转的bug解决

编程式路由导航在跳转到当前路由组件(跳转的路由不变),而且参数也不变的情况下,多次执行会报错,比如我点击搜索,参数没有变化,点击两次控制台会输出:

Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/search?keywordUpper=".

因为vue-router3.1.0之后,引入了Promise语法,如果没有通过参数指定成功的回调或者失败的回调就会返回一个Promise实例对象(可以变量接收push方法的返回值查看),且内部会判断如果要跳转的路径和参数都没有改变,会抛出一个失败的Promise实例,控制台就会报错

不完美的解决方式:

  1. push()方法或者replace()方法的第二个参数和第三个参数指定回调函数(指定其中一个就可以)
  2. 使用catch语法进行处理
// 1. 第一种处理方式的编程式路由导航回调函数
toSearch() {
  this.$router.push({
    name: "search",
    params: { keyword: this.keyword || undefined },
    query: { keywordUpper: this.keyword.toUpperCase() },
  },()=>{},()=>{});
},
// 2. 第二种处理方式的编程式路由导航回调函数
toSearch() {
  this.$router.push({name: "search",
    params: { keyword: this.keyword || undefined },
    query: { keywordUpper: this.keyword.toUpperCase() },
  }).catch(()=>{});
},

完美的解决方式,通过重写VueRouter原型对象上的方法来解决:

  1. VueRouter式路由器对象的构造函数,而pushreplace等方法就在该构造函数的原型对象上
  2. 通过this.$router.push()调用方法的时候,其实是通过VueRouter的实例掉用其原型对象上的方法
  3. 也就是this.$routerVueRouter的实例化对象

在路由器对象文件(router目录下的index.js文件)中暴露路由器对象之前,重写其原型对象上的方法:

// ... 引入VueRouter插件
const originPush = VueRouter.prototype.push
const originReplace = VueRouter.prototype.replace
// location就是调用push跳转路由的时候以对象形式传递的内容,这里的函数千万不能使用箭头函数
VueRouter.prototype.push = function(location,onResolved,onRejected){
  // 没有传递回调
  if(onResolved === undefined && onRejected === undefined){
    // 手动传递回调处理
    // return originPush.call(this,location,()=>{},()=>{})
    // 使用catch处理,返回的内容还是原来的方法返回的内容
    return originPush.call(this,location).catch(()=>{})
  }else{
    return originPush.call(this,location,onResolved,onRejected)
  }
}
VueRouter.prototype.replace = function(location,onResolved,onRejected){
  // 没有传递回调
  if(onResolved === undefined && onRejected === undefined){
    // 手动传递回调处理
    // return originPush.call(this,location,()=>{},()=>{})
    // 使用catch处理,返回的内容还是原来的方法返回的内容
    return originReplace.call(this,location).catch(()=>{})
  }else{
    return originReplace.call(this,location,onResolved,onRejected)
  }
}
export default new VueRouter({
  // ...配置路由
})

关于这里this的理解:

  1. 当这样调用this.$route.push()方法的时候,调用的是我们自己定义在原型上的方法
  2. this就是调用该方法的VueRouter实例对象
  3. 但是在这个方法的内部,调用了我们开始存储好的originPush方法,该方法是定义在全局的,作为函数调用this是window
  4. 所以我们应该使用call()改变this,让this变成VueRouter的实例对象
  5. 在定义方法的时候不能使用箭头函数,因为箭头函数没有自己的this,this就不是VueRouter的实例对象,而是找到外层的window(这里应该是因为语法的问题,不允许this指向window,所以得到的undefiend)
let obj = {
  // show: function () {
  //   console.log(this)
  // }
  show:()=>{
    console.log(this)
  }
}
// 虽然是obj调用show方法,但是show为箭头函数,所以没有this,this指向是window
obj.show()
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本资源为网传资源,为一套比较全的项目,系统架构采用SOA dubbo+EasyI实现,资源过大上传乃是下载链接,如有侵权请联系/留言,不多说下面上目录: 1.01 系统介绍2 f% Z/ C0 z% {1 l" q+ R 2.02 类型介绍6 r6 J' |. a2 F, t6 y; _. v# p2 J# ` 3.03 系统演示介绍! D$ C$ a1 K( q, c 4.04 数据结构介绍 5.05 数据结构设计器8 q9 r1 t3 Q( A# T" ?# P0 w+ n 6.06 数据准备 7.07 后台管理框架搭建 8.08 spu管理跳转 9.09 分类下拉列表的加载 10.10 品发布的业务逻辑 11.1.建表 12.2.导数据 13.3.统一环境 14.4.生成js文件0 ~* z9 y2 K/ u; k+ N8 S# l* H: V! Y 15.01 品spu参数提交- Q( T& z* U- B, ^: q) _ 16.02 品spu图片上传服务介绍' ], M1 {0 w- x; Z' L. y 17.03 文件上传 工具 18.04 spu信息发布功能) G' E R g0 B( \* X + N& D7 p 19.05 spu动态图片追加% N0 H) M. o8 ~+ A 20.06 spu动态图片追加 21.07 属性功能管理介绍 22.08 属性功能管理介绍 23.09 属性保存功能跳转 24.10 属性双重集合参数3 Z4 [5 |4 D, k, M- G 25.11 属性保存功能业务层代码3 E: v6 D1 y: N* t5 X& Q, | 26.12 属性能业异步内嵌页8 t- |* |7 r# @ 27.13 属性集合查询) q& ~/ W) _9 c* r+ d: D5 Z% Y 28.01 属性的双重集合查询语句 29.02 sku功能介绍! m- K+ b0 K7 U* B! o* g3 S 30.03 sku功能跳转 31.04 客户端js函数中的el表达式 32.05 异步加载spu列表数据# M& R, \7 \3 y z- w+ a% }( g" ^ 33.06 用复选框操作属性列表显示 D+ k( T; J. J" `2 u 34.07 属性参数的提交) O0 o, s0 X \! P! @0 b& H 35.08 sku的数据结构说明8 m! S+ i9 k) W4 F# ?7 U 36.09 sku添加的业务实现1 |' b G% [! w/ J* C n; N 37.10 easyui的介绍 38.11 easyui的layout初始化介绍- v- C+ z# J) e- O* I! G 39.12 easyui手风琴控件介绍1 X/ X# \' k% x2 N 40.13 tree控件 41.14 tab控件: U, |: a! S7 ?: A$ t1 ?, G 42.1.properties% C2 @ d! }9 Z& ^2 c 43.2.主键9 D3 f9 W4 J* C" B4 M 44.3. 锚点. F( v2 C8 q- I# F+ G 45.01 数据表格的用法: e: S' z. T0 @8 T( `6 L 46.02 combobox的用法; _6 }2 p3 v: O7 l* } 47.03 嵌套布局的用法% L! U! L0 ]% B 48.04 easyui同步提交后跳转问题 49.05 乱码问题! {6 |( X* i) u; w! Y' B 50.06 首页初始化 51.07 用户登录方法 k, `) e/ R( F& m5 X 52.08 通过cookie取得用户的个性化信息, J7 ?0 ]' A) \6 E7 n5 K 53.09 通过客户端cookie取得用户的个性化信息0 i7 ], O/ }# H: t6 @: j) I+ i 54.10 用户个性化信息9 b% h5 O% p; I" P7 z 55.11 任务总结 56.01 品检索介绍 57.02 品分类检索介绍7 J% Q6 L& r: T6 N. H8 a- E6 Y% f" d" S4 I 58.03 品分类检索sql: Z- N/ w: k; }2 b9 E( w8 I 59.04 品分类检索列表 60.05 品属性检索介绍 61.06 ajax字符串数组传参 62.07 ajax字符串json传参& i' g9 \7 ^! e7 _ 63.08 表单序列化传参 64.09 动态sql的设计方法" o# D. ?# R% _1 X 65.10 动态sql的实现 66.11 任务 67.1.项目演示 68.2.resu
尚硅谷的Vue路由笔记主要包括在app.vue中设置路由出口、开启replace模式以及引入VueVue Router等内容。在app.vue中,我们可以使用<router-view></router-view>标签来设置路由出口,用于展示不同路由对应的组件。要开启replace模式,可以在<router-link>标签中添加replace属性,例如<router-link replace ...>News</router-link>这样点击路由链接时,不会添加新的浏览器历史记录,而是替换当前的记录。在使用Vue Router之前,我们需要先引入VueVue Router库。可以通过以下代码来引入:import Vue from 'vue'; import VueRouter from 'vue-router'。这样就可以在项目使用VueVue Router来实现路由导航的功能了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [vue项目笔记(尚硅谷)](https://blog.csdn.net/m0_46233342/article/details/123071857)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [【笔记整理】Vue Router路由管理器详解(尚硅谷vue学习资料总结)](https://blog.csdn.net/fangyuan__/article/details/126075358)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值