你可以通過右下角的 按鈕切換為簡體顯示


前端Vue核心

开发一个前端模块可以概括为以下几个步骤:
(1)写静态页面、拆分为静态组件;
(2)发请求(API);
(3)vuex(actions、mutations、state三连操作);
(4)组件获取仓库数据,动态展示;

Vue文件目录分析

脚手架创建项目

1
vue create app

node_modules:放置项目依赖的地方
public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面
src:程序员源代码文件夹
assets文件夹:经常放置一些静态资源(一般防止多个组件公用的静态资源),放置在assets文件夹里面资源,webpack进行打包的时候,webpack会把静态资源当作一个模块打包js文件里面
components文件夹:一般放置非路由组件(全局组件)
App.vue: 唯一的根组件
main.js :入口文件【程序最先执行的文件】
babel.config.js:babel配置文件
package.json:认为项目‘身份证’,记录项目叫什么,项目当中有哪些依赖、项目怎么运行
README.md:项目说明文件

项目配置

项目运行浏览器自动打开

package.json文件

1
2
3
4
5
6
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},

关闭eslint校验工具

根目录下创建vue.config.js,进行配置,需要对外暴露

1
2
3
4
5
module.exports = {
//关闭eslint
lintOnSave: false
}

src文件夹别名设置

因为项目大的时候src(源代码文件夹):里面目录会很多,找文件不方便,设置src文件夹的别名的好处,找文件会方便一些
创建jsconfig.json文件,用@/代替src/,exclude表示不可以使用该别名的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
//@不能在node_modules、dist使用
"exclude": [
"node_modules",
"dist"
]
}

项目路由的分析

1
cnpm i vue-router@3

前端路由: KV键值对
key:URL(地址中的路径)
value: 相应的路由组件
注意:项目上中下结构
Home首页路由组件、Search路由组件、login登录路由
非路由组件
Header【首页、搜索页】、Footer【首页、搜索页】 登录|注册页面没有

组件页面样式

组件页面的样式使用的是less样式,浏览器不识别该样式,需要下载相关依赖
cnpm install --save less less-loader@5
如果想让组件识别less样式,则在组件中设置
<script scoped lang="less">

清除Vue页面默认的样式

vue是单页面开发,我们只需要修改public下的index.html文件

<link rel="stylesheet" href="<%= BASE_URL %>reset.css">

pages文件夹

创建pages文件夹,并创建路由组件
创建router文件夹,并创建index.js进行路由配置,最终在main.js中引入注册
路由组件非路由组件区别:
非路由组件放在components中,路由组件放在pagesviews
非路由组件通过标签使用,路由组件通过路由使用
不管路由路由组件、还是非路由组件身上都有$route|$router属性

main.js注册完路由,所有的路由和非路由组件身上都会拥有$router$route属性
$router:一般进行编程式导航进行路由跳转【push | replace】
$route: 一般获取路由信息【路径、query、params】
路由跳转方式
声明式导航router-link标签 ,可以把router-link理解为一个a标签,它 也可以加class修饰,务必要有to属性
编程式导航 :push|replace可以进行路由的跳转,声明式导航能做的编程式都能做,除了可以进行路由跳转,还可以处理一些业务逻辑
重定向:在项目跑起来的时候访问/定向到首页

1
2
3
4
{
path: '*',
redirect: "/home"
}

footert组件显示与隐藏

footer在登录注册页面是不存在的,显示或隐藏组件:v-if| v-show
这里使用v-show,因为v-if会频繁的操作dom元素消耗性能,v-show只是通过样式将元素显示或隐藏
Footer组件: 在Home、Search显示Footer组件
Footer组件: 在登录注册时候隐藏
<Footer v-show="$route.path=='/home' || $route.path=='/search'"></Footer> (不推荐!!!)
配置路由的时候可以给路由添加路由元信息【meta】,路由需要配置对象
meta:{show:true}
<Footer v-show="$route.meta.show"></Footer> 路由元信息

路由传参

queryparams两个属性可以传递参数
query参数:不属于路径当中的一部分,类似于get请求,地址栏表现为 /search?k1=v1&k2=v2
query参数对应的路由信息 path: "/search"
params参数:属于路径当中的一部分,需要注意,在配置路由的时候,需要占位 ,地址栏表现为 /search/v1/v2
params参数对应的路由信息要修改为path: "/search/:keyword" 这里的/:keyword就是一个params参数的占位符
路由传递参数:
第一种:字符串形式
this.$router.push("/search"+this.keyword="?k="+this.keyword.toUpperCase())
第二种:模板字符串
`this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`)'
第三种:对象 (常用)
需要给路由一个name属性
this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}})

路由传递参数(对象写法)path是否可以结合params参数一起使用?

路由跳转传参的时候,对象的写法可以是name、path形式,需要注意的是,path这种写法不能与params参数一起使用
this.$router.push(path:’/search’,params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()})

如何指定params参数可传可不传?

配置路由的时候占位了(params参数),不传递params参数,url有问题
this.$router.push({name:’search’,query:{k:this.keyword.toUpperCase()}})

1
2
3
4
5
6
7
8
{
// 指定params中?可传可不传
path: "/search/:keyword?",
component: Search,
meta: { show: true },
name: "search"
},

params参数可以传递也可以不传递,但是如果传递是空串,如何解决?

使用undefined解决:params参数可以传递、不传递(空的字符串)
this.$router.push({name:’search’,params:{keyword:’’ || undefined },query:{k:this.keyword.toUpperCase()}})

路由组件传递props数据

this.$router.push({name:”search”,params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}})

  • router/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  {
// 指定params中?可传可不传
path: "/search/:keyword?",
component: Search,
meta: { show: true },
name: "search"
//路由组件能不能传递props数据?
//布尔值写法:只有params
props:true,
//对象写法: 额外的给路由组件传递一些props 该对象中的所有key-value都会以props的形式传给search组件
props:{a:1,b:2},
//函数写法:可以传递params参数、query参数,通过props传递给路由组件search
props:($route)=>{
return {keyhword:$route.params.keyword,k:$route.query.k};
}
},
  • search.vue(接收数据)
1
props:['keyword','a','b','k']

多次执行相同的push问题

编程式导航路由跳转到当前路由(参数不变), 多次执行会抛出NavigationDuplicated的警告错误?
声明式导航没有这类问题
为什么会出现这种现象:
由于vue-router引入了promise,当传递参数多次且重复,会抛出异常,因此出现上面现象,
第一种解决方案:是给push函数,传入相应的成功的回调与失败的回调
this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}},()=>{},(error)=>{})
第一种解决方案可以暂时解决当前问题,将来在别的组件当中使用push|replace还是会出现类似现象,因此我们需要从‘根’治病;
第二种:重写pushreplace方法
router index.js
//call || apply 区别
相同点:可以调用函数一次,都可以篡改函数的上下文一次
不同点: call与apply传递参数:call传递参数用逗号隔开,apply方法执行,传递数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

//先把VueRouter.prototype身上的push|replace方法进行保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写VueRouter.prototype身上的push方法了
VueRouter.prototype.push = function(location, resolve, reject) {
//第一个形参:路由跳转的配置对象(query|params) 告诉原来的push往哪跳转
//第二个参数:undefined|箭头函数(成功的回调)
//第三个参数:undefined|箭头函数(失败的回调)
if (resolve && reject) {
//push方法传递第二个参数|第三个参数(箭头函数)
//originPush:利用call修改上下文,变为(路由组件.$router)这个对象,第二参数:配置对象、第三、第四个参数:成功和失败回调函数
originPush.call(this, location, resolve, reject);
} else {
//push方法没有产地第二个参数|第三个参数
originPush.call(
this,
location,
() => {},
() => {}
);
}
};
//重写VueRouter.prototype身上的replace方法了
VueRouter.prototype.replace = function(location, resolve, reject) {
if (resolve && reject) {
originReplace.call(this, location, resolve, reject);
} else {
originReplace.call(
this,
location,
() => {},
() => {}
);
}
};


Promise

NavigationDuplicated

定义全局组件

由于三级联动,在Home、Search、Detail出现,把三级联动注册为全局组件。 只需注册一次!全局配置在main.js中配置

1
2
3
4
//将三级联动组件注册为全局组件
import TypeNav from '@/pages/Home/TypeNav';
//第一个参数:全局组件名字,第二个参数:全局组件
Vue.component(TypeNav.name,TypeNav);

在Home组件中使用该全局组件

1
2
3
4
5
6
7
<template>
<div>
<!-- 三级联动全局组件已经注册为全局组件,因此不需要引入-->
<TypeNav/>
</div>
</template>

三级联动组件

代码改变时实现页面自动刷新

根目录下配置vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
//关闭eslint
lintOnSave: false,
devServer: {
// true 则热更新,false 则手动刷新,默认值为 true
inline: true,
// development server port 8000
port: 8000,

}
}

配置文件后,要重启下项目

Home首页其他组件

home文件夹index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
<div>
<!-- 三级联动全局组件已经注册为全局组件,因此不需要引入-->
<TypeNav/>
<!-- 轮播图列表-->
<ListContainer/>
<!-- 今日推荐-->
<Recommend/>
<!-- 商品排行-->
<Rank/>
<!-- 猜你喜欢-->
<Like/>
<!-- 楼层 -->
<Floor/>
<Floor/>
<!-- 商标-->
<Brand/>
</div>
</template>

<script>
import ListContainer from './ListContainer'
import Recommend from './Recommend'
import Rank from './Rank'
import Like from './Like'
import Floor from './Floor'
import Brand from './Brand'
export default {
name: "index",
components: {
ListContainer,
Recommend,
Rank,
Like,
Floor,
Brand,
}
}
</script>

<style scoped>

</style>

二次封装axios

为什么要二次封装axios?
请求拦截器:可以在发请求之前可以处理一些业务,响应拦截器:当数据返回以后可以处理一些事情
axios中文文档: http://axios-js.com/zh-cn/docs/index.html
在根目录下创建api文件夹,创建request.js文件。

1
cnpm install --save axios
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import axios from "axios";
//1、对axios二次封装
// 利用axios对象的方法create,去创建一个axios实例
const requests = axios.create({
//基础路径,requests发出的请求在端口号后面会跟改baseURl
baseURL:'/api',
timeout: 5000,
})
//2、配置请求拦截器
requests.interceptors.request.use(config => {
//config内主要是对请求头Header配置
//比如添加token

return config;
})
//3、配置相应拦截器
requests.interceptors.response.use((res) => {
//成功的回调函数
return res.data;
},(error) => {
//失败的回调函数
console.log("响应失败"+error)
return Promise.reject(new Error('fail'))
})
//4、对外暴露
export default requests;

前端通过代理解决跨域问题

在根目录下的vue.config.js中配置,proxy为通过代理解决跨域问题。
我们在封装axios的时候已经设置了baseURL为api,所以所有的请求都会携带/api,这里我们就将/api进行了转换。如果你的项目没有封装axios,或者没有配置baseURL,建议进行配置。要保>证baseURL和这里的代理映射相同,此处都为’/api’。
webpack官网相关知识解读
网站中的webpack.config.js就是vue.config.js文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
//关闭eslint
lintOnSave: false,
devServer: {
// true 则热更新,false 则手动刷新,默认值为 true
inline: false,
// development server port 8000
port: 8000,
//代理服务器解决跨域
proxy: {
//会把请求路径中的/api换为后面的代理服务器
'/api': {
//提供数据的服务器地址
target: 'http://39.98.123.211',

}
},
}
}

⚡ 尚品汇前台学习笔记(二)