文章目录
1.vue文件目录分析
node_modules文件夹:项目依赖文件夹。
public文件夹:一般放置一些静态资源(如:图片),webpack打包时会原封不动的打包到dist文件夹中。
src文件夹(源代码文件夹):
- assets文件夹:一般也是放置静态资源,(如:放置多个组件共用的静态资源),webpack在打包时,会把这些静态资源当成时一个模块,打包在js文件里面。
- components文件夹:一般放置的是非路由组件(全局组件)。
- App.vue:唯一的根组件,Vue当中的组件(.vue)。
- main.js:程序入口文件,也是整个程序中最先执行的文件。
babel.config.js:配置文件(babel相关)。
package.json文件:记录项目的详细信息,如项目中有哪些依赖、如何运行、名称等。
package-lock.json:缓存性文件,记录了当前项目所依赖的模块版本。
2.项目的配置
2.1开发环境打包完成,浏览器自动打开
//package.json中
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build"
}
2.2 eslint校验功能关闭
//在根目录下,创建一个 vue.config.js 文件
module.exports = {
lintOnSave:false
}
2.3 src文件夹简写方法,配置别名
//在根目录下,创建jsconfig.json 配置别名@,用@/ 替代src/ ,在文件数量多,嵌套层次多的时候尤为方便
//exclude表示的是不可使用该别名的文件
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*":["src/*"]
}
},
"exclude": ["node_module","dist"]
}
3.组件的使用
3.1 非路由组件的使用步骤
- 在components文件夹中创建组件
- 在其他组件中引入并且注册组件
- 已标签的形式使用
3.2 路由组件的使用
- 在views/pages文件夹中创建路由组件
- 在router文件夹中index.js中配置路由
3.3 路由组件和非路由组件区别:
- 非路由组件放在components中,路由组件放在pages或views中
- 非路由组件通过标签使用,路由组件通过路由使用
- 在main.js注册完路由,所有的路由和非路由组件身上都会拥有$router $route属性
- $router:是vueRouter的实例对象,是一个全局的对象,他包含了所有的路由,包含了许多关键的对象和属性
- $route: 每一个路由都会有一个 $route对象,是一个局部的对象,可以获取对应的name,path,params,query等
3.4 Footer组件的显示与隐藏
- 可以使用v-if 或v-show,因为v-if频繁操作Dom消耗性能,v-show只是对元素的显示或隐藏这里使用v-show
- 给路由配置元信息meta,在元信息中定义show属性,值为布尔值
{
path: '/home',
component: Home,
meta: {
show: true
}
},
{
path: '/login',
component: Login,
meta: {
show: false
}
}
- 将show的值赋给v-show,从而按需展示隐藏Footer组件
- 路由配置对象的key不能瞎写
4.路由传参
4.1 路由跳转的方式
- 声明式导航
//必须有to,可以把router-link理解为一个a标签,它 也可以加class修饰
<router-link to="/"></router-link>
- 编程式导航
//编程式导航除了路由跳转,在跳转之前还可以处理一些业务逻辑
this.$router.push('/')
4.2 路由传参
- 方法一:字符串或模板字符串传参
- 方法二:对象形式传参
- 注意:
query参数:不属于路径当中的一部分,类似于get请求,地址栏表现为 /search?k1=v1&k2=v2
query参数对应的路由信息 path: “/search”
params参数:属于路径当中的一部分,需要注意,在配置路由的时候,需要占位 ,地址栏表现为 /search/v1/v2
params参数对应的路由信息要修改为path: “/search/:keyword” 这里的/:keyword就是一个params参数的占位符 - 路由传递参数(对象写法),如果我们传参中使用了params,只能使用name,不能使用path,如果只是使用query传参,可以使用path,所以,路由需要配置name字段,传参的语法为
this.$router.push({name:'search',params:{},query:{} })
- 指定params参数可传可不传
//在占位符后加 ?即可
path('/search/:keyWord?')
- params参数可传可不传,但如果传递的是空串,跳转地址信息则缺少/search,如何解决呢?
this.$router.push({name:'search',params:{keyWord:''||undefined})
- 路由组件能不能传递props数据?
答案是可以的:
props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给组件;
props值为对象,该对象中所有的key-value的组合最终都会通过props传给组件;
props值为函数,该函数返回的对象中每一组key-value都会通过props传给组件
5.多次push相同路由报错问题
//在router的index.js文件中添加如下代码
import Vue from 'vue'
import VueRouter from 'vue-router'
//解决重复push相同路由报错问题
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
6. axios二次封装
6.1 为什么需要二次封装axios
//配置请求拦截器、响应拦截器,可以在发送请求之前处理一些业务、响应拦截器可以在数据返回之后处理一些事情
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
6.2 自定义axios实例
//可以使用自定义配置新建一个实例
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
7.请求接口统一封装
7.1 在api文件夹中创建index.js文件,用于封装所有请求,将每个请求封装为一个函数并暴露出去,组件只需要调用相应函数即可
import requests from "@/api/request";
//首页三级分类接口
export const reqCateGoryList = () => {
return requests({
url: '/product/getBaseCategoryList',
method: 'GET'
})
}
import {reqCateGoryList} from './api'
//发起请求
reqCateGoryList();
7.2 前端通过代理解决跨域问题
//在vue.config.js中配置
module.exports = {
//关闭eslint
lintOnSave: false,
devServer: {
// true 则热更新,false 则手动刷新,默认值为 true
inline: false,
// development server port 8000
port: 8001,
//代理服务器解决跨域
proxy: {
//会把请求路径中的/api换为后面的代理服务器
'/api': {
//提供数据的服务器地址
target: 'http://39.98.123.211',
}
},
}
}
8.vuex的使用
8.1 vuex的基本使用
//安装并引入vuex,根目录创建store文件夹,创建index.js文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//创建store仓库并暴露出去
export default new Vuex.Store({
//state包含组件的状态,是数据源
//注意:vuex中的数据不是永久的,页面刷新后,数据会被重置
state:{
count: 0
},
//更改store中状态的唯一方法是提交mutation,在组件中通过method提交mutation
mutations:{
increment(state) {
return state.count++
}
},
})
//在main.js中引入上述文件,并在vue实例中注册store
import store from './store'
new Vue({
render: h => h(App),
//注册路由,此时组件中都会拥有$router $route属性
router,
//注册store,此时组件中都会拥有$store
store
}).$mount('#app')
8.2 在 Vue 组件中获得 Vuex 状态
//方法一 ,在组件中通过computed返回state中数据
computed: {
count() {
//state数据是响应式的,通过计算属性将获取的数据返回即可
return this.$store.state.count;
},
},
//方法二 ,使用mapState辅助函数,当一个组件需要多个状态都声明为计算属性会比较冗余,可以使用辅助函数生成计算属性
computed: mapState(['count']),
8.3 更改state中的状态
- 提交mutation是变更state状态的唯一途径,state中的数据不可以直接更改,需要通过提交mutation来更改状态
- commit mutation的行为在组件的methods节点中触发
- 好处:可以追踪数据的变更,方便后期的维护
- 实例:
//在mutations中定义更改数据的方法
mutations:{
//除了传入state数据源还可以传入额外的参数
increment(state) {
return state.count++
}
}
//在组件中提交变更
//方法一 , this.$store.commit
methods: {
addCount() {
//在方法中通过提交一个mutation的方式提交变更
this.$store.commit('increment');
},
}
//方法二 , 使用mapMutations辅助函数将methods映射为 store.commit 调用,在组件中直接使用increment作为事件处理函数即可
methods: {
...mapMutations(['increment']),
}
8.4 异步操作更改state状态
- action用于处理异步任务
- mutation只能处理同步任务,异步任务在actions中定义,action不能直接更改state,如果需要异步操作更改state,需要在action中提交mutation
- 如何在组件中触发异步变更呢?
actions: {
//action不能直接修改state,需要通过提交mutation变更state
incrementAsync(context) {
setTimeout(() => {
context.commit('increment')
},1000)
}
}
//方法一 , this.$store.dispatch
methods: {
addCountAsync() {
//在组件中触发异步函数
this.$store.dispatch('incrementAsync');
},
},
//方法二 ,使用mapActions
methods: {
...mapActions(['incrementAsync']),
},
8.5 模块式开发——modules
- 组件中所有的状态都集中到同一个仓库中,会非常臃肿,所以可以将store分割成模块(小仓库),每个module都会有自己的state、mutation、action、getter
- 步骤:
//在store中创建小仓库
import { getBaseCategoryList } from '@/api/index.js'
export default {
state: {
categoryList: []
},
mutations: {
getCateList(state, categoryList) {
state.categoryList = categoryList
console.log(state.categoryList);
}
},
actions: {
async getCateList(context) {
const { data: res } = await getBaseCategoryList()
console.log(res);
if (res.code !== 200) {
return
}
context.commit('getCateList', res.data)
}
}
}
//在大仓库中导入小仓库
import home from '@/store/home/index.js'
import search from './search';
export default new Vuex.Store({
modules: {
home,
search
},
});
//在组件中获取状态
this.$store.state.home.categoryList;
9.防抖动和节流
9.1 防抖动
- 安装并引入lodash
//安装
npm i -S lodash
//引入
import _ from 'lodash'
//防抖,前面所有的触发都会被取消,只会执行最后一次触发,到达规定延迟时间后,调用回调函数,返回新的 debounced(防抖动)函数
let debounced = _.debounce(function(){},1000)
9.2 节流
- 在规定的间隔时间内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁的触发变为少量的触发
//表示在1秒内只触发一次回调
_.throttle(function(){},1000)
10.mockjs使用步骤(如果想要模拟数据,需要用到插件mockjs)
- 在项目src文件夹下创建mock文件夹;
- 准备JSON数据,在mock文件夹中创建相应的JSON文件——注意格式,不要留空格;
- 把mock需要的图片放到public文件夹下(public文件夹在项目打包时会原封不动把资源打包到dist中);
- 在mock文件夹下新建mockServe.js文件,使用mockjs的mock方法实现模拟数据
import Mock from 'mockjs';
import banners from './banners.json';
import floors from './floors.json';
//mock方法第一个参数是路径,第二个参数是假数据
Mock.mock('/mock/banners', banners);
Mock.mock('/mock/floors', floors);
- 在入口文件main.js中引入mockServe.js
11.组件间通信
//父->子 (props)
//子组件
export default {
name: 'Floor',
props: ['floorList']
};
//父组件 绑定floorList属性并传值
<Floor :floorList="item"></Floor>
//子->父 ($emit)
//子组件
methods: {
sendTrademark(trademark) {
this.$emit('trademarkInfo', trademark);
},
}
//父组件 绑定自定义事件
<SearchSelector @trademarkInfo="trademark"></SearchSelector>
methods: {
trademark(trademark) {
this.searchParams.trademark = `${trademark.tmId}:${trademark.tmName}`;
this.dispatchSearch();
}
}
//非父子组件之间 (evenBus)
//先在main.js中创建bus
new Vue({
router,
store,
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}
}).$mount('#app')
//A组件
removeKeyword() {
this.$bus.$emit('clear');
}
//B组件
mounted() {
this.$bus.$on('clear', () => {
this.iptValue = '';
});
}
12.数组的一些方法 let arr =[‘1’]
12.1 push()和pop()
- push(): 可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。 语法:
const count = arr.push('2','3')
,返回count=3,此时arr=['1','2','3']
- pop():数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项,如果数组为空则返回undefined。语法:
const remove = arr.pop()
,返回remove=3,此时arr=['1','2']
- 注意:pop()不需要参数,默认移除数组最后一项。
12.2 shift() 和 unshift()
- shift():删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 。
- unshift:将参数添加到原数组开头,并返回数组的长度 。
- 注意,同push,pop用法相同,一个针对末尾一个针对开头的区别。都是修改了原数组
12.3 concat()
- concat() :将参数添加到原数组中。这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 concat()方法传递参数的情况下,它只是复制当前数组并返回副本。该方法没有修改原数组。
- 语法:
const newArr = arr.concat('4')
返回新数组['1', '2', '4']
,原数组并未被修改arr=['1', '2']
12.4 slice()
- slice():返回从原数组中指定开始下标到结束下标之间的项组成的新数组。slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。
- 在只有一个参数的情况下, slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。
- 如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。
12.5 splice()
- splice():很强大的数组方法,它有很多种用法,可以实现删除、插入和替换。
- 删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。例如, splice(0,2)会删除数组中的前两项。
- 插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数)和要插入的项。例如,splice(2,0,4,6)会从当前数组的位置 2 开始插入4和6。
- 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice (2,1,4,6)会删除当前数组位置 2 的项,然后再从位置 2 开始插入4和6。
- splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回一个空数组。
12.6 indexOf()和 lastIndexOf()
- indexOf():接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的开头(位置 0)开始向后查找。
- lastIndexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的末尾开始向前查找。
- 这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符。
12.7 过滤 filter()
- 语法:
const a = arr.filter((item)=>return item>1)
,返回a=['2']
,此时arr=['1', '2']
。 - 使用filter()会生成一个新的数组,且新数组的变化不会影响原数组。
13.全局守卫
13.1 前置守卫
router.beforeEach((to,from,next)=>{
//to:可以获取你要跳转到的路由信息
//from:可以获取你从哪个路由而来的信息
//next:next()放行 next('/')放行到具体的路由 next(false)驳回
})