模块化开发
浏览器只支持ES6
的模块化,其他的需要使用webpack
处理后才能在浏览器上使用
模块化是将每个js
文件作为一个模块导入或者导出,解决同时引用多个js
文件时,变量重名的问题,到后来扩展为非js
文件也能作为模块进行导入导出。
export(导出)/import(导入):
1 | 1. html中引用时,添加引用类型 |
export default/import:
导入者可以对导出的内容重新命名,只能有一个default
1 | 1. html中引用时,添加引用类型 |
其他
- 统一全部导入:
import * as name from './aaa.js'
,通过name.变量
,name.函数
,name.类
取对应数据 import
后不写路径的话是从mode_modules
中直接引用模块
webpack
开发代码 –> webpack处理下 –> 可部署。不然有些文件浏览器不支持开发代码的语法
- 核心:让我们能进行模块化开发,并帮助我们处理模块间的依赖关系。不仅打包JavaScript文件。css、image、json等都能当作模块来使用
- 用法:
1
2
3
4
5
6
7
8
9
10
11
121. 文件夹目录:
|-- dist
|-- src
|-- main.js main.js导入aaa.js,导入导出语法看上面的ES6导入导出方式
|-- aaa.js aaa.js导入bbb.js
|-- bbb.js
|-- index.html
2. 使用webpack处理src中的依赖关系
webpack ./src/main.js ./dist/result.js
命令行的第一个文件是入口,第二个文件是生成的文件。如果配置了webpack.config.js,可以在里面配置入口文件和生成文件路径
3. 在 index.html 中引用生成的文件
<script src="./dist/result.js"></script>
vue-cli
需要注意的是,vue-cli
是基于webpack
的,与webpack
不同,它尽量减少用户配置文件,将配置文件隐藏。尤其package.json
中的"@vue/cli-service": "^4.4.0"
管理了很多包
配置文件
vue.config.js
配置:alias
配置别名:默认'@':'src'
,这样引用的时候,直接使用别名就行了
.editorconfig
: 配置一些代码风格等,使用的时候前面加个~
ES6中函数的写法
1 | 1. 定义函数的方式 |
网页开发的发展路程
JSP
阶段:后端渲染:请求页面时,后端返回整个被渲染好的页面(html+css+js
),然后整体返回。后端路由:在后端将一个url
和一个页面映射- 前后端分离:
- 后端:不进行渲染,只负责提供数据,后端分为静态资源服务器和提供
API
接口的服务器 - 后端路由:仍然是后端路由阶段
- 过程:输入
url
–> 从静态服务器请求(html+css+js
) –>js
由浏览器执行 –>ajax
从API
服务器请求数据,局部更新
- 后端:不进行渲染,只负责提供数据,后端分为静态资源服务器和提供
SPA
页面:单页富应用,整个页面只有一个html
页面- 后端:静态资源服务器只有一个
html+css+js
,API
服务器继续提供接口 - 前端:前端路由,前端根据不同的
url
从得到的html+css+js
中抽离出对应的页面 - 过程:输入主页 –> 从静态服务器请求一个(
html
) –> 再请求其他url
–> 从前面得到的资源抽离出来
- 后端:静态资源服务器只有一个
Router:Vue的前端路由
基础知识
- 实现的底层
url
的hash
更改,html5
的history
设置 vue-router
可把一个组件映射成一个url
页面,每个.vue
文件就是一个组件(包括html+css+js
)router-link
补充:1
2
3tag: 指定<router-link>之后渲染成什么组件,比如<router-link tag="li">
replace: replace不会留下history记录,浏览器后退键不能使用,<router-link replace>
active-class: router-link自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称。或者在创建router时,直接在router里面用linkActiveClass:'name'- 使用原生标签,不用
router-link
做路由跳转:1
2
31. 在button(或其他标签)绑定函数
2. 在methods中实现绑定的函数
3. 在绑定的函数中使用this.$router.push('/url')或者replace('/url') vue-router
为每个vue
组件都绑定了注册的router
,可以通过\$router
引用。动态路由中的\$route
和\$router
不同,$route
表示现在活跃的路由。这还是因为所有组件都继承自vue
的原型,vue
原型中的属性和方法,vue
组件都有。
路由的懒加载
路由的懒加载:路由中通常定义不同的页面,这页面最后会被打包到一个js文件中,如果一次请求,需要太长时间,可以使用懒加载方式解决
1 | 1. 使用箭头函数 |
路由嵌套
路由嵌套:比如在/home
中分为/home/news
和/home/message
1 | 1. 在home下创建子路由 |
动态路由
动态路由:对于用户/user/zhangsan
和/user/lisi
组件一样,数据不一样,处理方式
1 | 1. 在router中配置 |
url参数传递
url
参数传递:除了上面的动态路由,也可以使用url
中的query
参数,query
就是url
中?
之后的部分
1 | 1. 使用 router-link |
路由守卫
路由守卫: 由一个路由跳转到另一个路由的过程,我们可以在里面实现一些动作
1 | beforeEach(function(to,from,next){ |
keep-alive
keep-alive:组件创建后不被销毁。使用keep-alive
的组件有两个方法activated
(激活)和deactivated
(非激活)
1 | <keep-alive> |
Promise
promise基本使用
1 | new Promise((resolve,reject)=>{ // resolve和reject是选的,不用的话可以不传 |
Promise核心
Promise核心:将异步请求代码和业务代码分离
1 | setTimeout(()=>{ |
Promise的all方法
场景:从两个(多个)url
中都获取到数据后再操作
1 | Promise.all([ |
Vuex
vuex对所有组件进行统一管理,使管理的组件之间能够获得其他组件的状态和属性。统一管理,便于维护。
使用方式(两种)
- 在创建vue项目的时候直接勾选使用vuex,如下图:
- 使用npm安装vuex
- 运行
npm i vuex -s
- 在项目的根目录下新增一个
store
文件夹,在该store
文件夹内创建index.js,src的目录结构如下图: - 初始化
store
中index.js
中的内容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
getters:{
},
actions: {
},
modules: {
}
}) - 将
store
挂载到当前项目的Vue实例当中去,打开main.js
1
2
3
4
5
6
7
8
9
10
11
12import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
- 运行
使用第1种和第2种的结果都是一样的,只不过第2种较为繁琐,有4个步骤
Vuex的核心内容
在初始化store
中的index.js
时,可以看到store
中一共有五个成员:
- state:存放状态,简单理解就是存储共享的数据
- mutations:state成员操作,简单理解就是对state中的共享数据操作
- getters:加工state成员给外界
- actions:异步操作,为了异步操作存在的,如:url请求
- modules:模块化状态管理
Vuex的工作流程
首先,Vue
组件如果调用某个Vuex
的方法过程中需要向后端请求时或者说出现异步操作时,需要dispatch
VueX
中actions
的方法,以保证数据的同步。可以说,action
的存在就是为了让mutations
中的方法能在异步操作中起作用。
如果没有异步操作,那么我们就可以直接在组件内提交状态的Mutations
中编写的方法来达成对state
成员的操作。**注意:不建议在组件中直接对state
中的成员进行操作,这是因为直接修改(例如:this.$store.state.name = 'hello')的话不能被
VueDevtools所监控到**。
Devtools`是vue在chrome中开发的一个插件,用于vue开发的调试。
最后被修改的state
成员会被渲染到组件的原位置当中去。
State
State
最简单的理解就是各个组件共享的数据。因为一个事物标识的状态是由其属性决定的,而属性由数据构成。每当数据发生变化时,属性也发生变化,进而状态就会发生变化。State
是单一状态树模式,也就是全局只有一个,类似单例模式。
Getters
可以对state
中数据加工后传递给外界Getters
中的方法有两个默认参数
- state:当前
Vuex
对象中的状态对象 - getters:当前getters对象,用于将getters下的其他getters拿来用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15states:{
math : 99,
english: 90
},
getters:{
mathScore(state){
return "数学:" + state.math
},
fullScore(state,getters){
return getters.mathScore + ",英语:" + state.english
}
}
// 组件中调用
this.$store.getters.fullScore
Mutations
mutations
是操作state
数据的方法的集合,比如对该数据的修改、增加、删除等等。
Mutations基本用法
mutations
方法都有默认的形参:([state] [,payload])
- state是当前VueX对象中的state
- payload是该方法在被调用时传递参数使用的
例如,我们编写一个方法,当被执行时,能把下例中的数学值修改:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15states:{
math : 99,
english: 90
},
mutations:{
setMathAdd1(state){
state.math ++
},
setEnglishAdd1(){ //可以不传state,默认有一个
english--
}
}
// 组件中调用mutation
this.$store.commit('setMathAdd1')
Mutations传值用法
在实际开发时,遇到在提交某个mutation
时需要携带一些参数给方法使用。
1 | states:{ |
增删state
中的成员
Vuex的sotre中的状态是响应式的,在mutations中都是对已有的数据进行修改是响应式的。但是直接使用delete
或者obj['newattr']=attr
进行增删数据时,Vue并不能进行实时响应。这是我们就要借助Vue.set()和Vue.delete()
1 | states:{ |
Actions
因为不建议在Mutations
中进行异步操作,所以Vuex
提供了Actions
专门进行异步操作,最终提交mutation
方法Actions
中的方法有两个默认参数
- context:与
store
实例具有相同方法和属性的context
对象(相当于this的指向) - payload:和mutations中的payload一样,是传入参数
假如我们有一个需求1:异步修改state
中的数据。分析这个需求,需要一个异步操作(必须放到action
中),一个修改state
数据的操作(必须通过mutations
中的mutation
实现)。
1 | states:{ |
假如我们有一个需求2:在需求1的基础上,如果异步操作执行成功,通知调用的组件执行相应的函数。这时我们使用Promise实现。
1 | states:{ |
Models
如果项目十分庞大,状态很多时,我们可以采用模块化管理。为了解决这个问题,Vuex
允许我们将store
分割成模块(module),每个模块有自己的states
,mutations
,actions
,getters
,甚至是嵌套子模块。
1 | const moduleA={ |
其他组件调用moduleA的mutation、getter、action,和之前一样。(这里必须子模块和根模块的函数名不同)
1 | this.$store.commit('increment') |
查看moduleA和moduleB的状态
1 | store.state.a // 可以使用DevTools查看,发现子模块其实就是store.state中的一个对象 |
通过执行 moduleA中aIncrement(context){}函数我们可以得到下面的打印
1 | context是个对象,包含{commit,dispatch,getters,rootGetters,rootState,state} |
moduleA中aIncrement(context){}函数也可以通过解构的方法写成下面(ES6语法,官网写法)
1 | aIncrement({state,commit,rootState}) |
规范目录结构
如果把整个store
都放在index.js
中不合理,所以需要拆分。较为合适的目录结构如下:
1 | store: |
对应的内容存放在对应的文件中,并使用export default{}
导出,在index.js
中引用。使用store
:在index.js
中存放并导出store
。
Axios:Axios实现基础是Promise
基本用法
1 | // axios(config)用法 |
axios并发请求
1 | // axios 发送并发请求,和Promise一样 |
aixos默认配置
使用axios.defaults
配置
1 | axios.defaults.baseURL = 'http://123.207.32.32:8080' |
axios封装
在每个组件中都导入的话,如果axios
不再维护,修改起来费事,因此可以封装
1 | // 新建一个js封装文件 |
axios拦截器
用于我们在发送每次请求或者得到响应后,进行对应的处理
1 | axiosInstance.interceptors.request.use(config=>{ |
Vue组件
如果请求数据时,可用外层的父组件请求数据,然后父组件将数据传递个子组件
父子组件间的参数传递
父 –> 子
使用props
进行传递:子组件定义props
属性用于接收传来的数据,父组件引用子组件时用v-bind
将父组件的数据传给子组件
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<div id="father">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{cmovies}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
const child = {
template:'#cpn',
// props: ['cmovies','cmessage'], //下面的方式也行
props:{
cmessage:{
type: String,
default: 'aaaaa'
}
},
data(){return{}},
methods:{}
}
const father = {
el:'#father',
data:{
return {
message:"你好啊",
movies:['海王','海贼王']
}
},
component: {cpn}
}
子 –> 父
子组件使用this.$emit
(“事件名称”,”事件参数”)向父组件发射事件,父组件使用v-on
接收
1 | <div id="father"> |
父子组件之间的访问
父组件访问子组件
1 | 1. 使用 $children 访问子组件 |
子组件访问父组件
1 | 1. 使用 $father 访问子组件的父组件 |
Slot插槽
vue
的slot
插槽:子组件中使用<slot>
占用一个位置,父组件调用子组件时,可以用不同的标签(如button
,span
)替换
匿名插槽
最简单的使用
1 | <div id="app"> //父组件app,并在父组件里面使用子组件cpn |
具名插槽
给插槽命名,解决多个插槽时,选择哪个插槽的问题
1 | 1. 匿名插槽的问题:如果有多个插槽,将全部替换 |
作用域插槽
父组件获得子组件的data并进行不同样式的渲染
1 | //下面是在父组件中引用 |
显示的结果:引用组件1处和2处展现的方式不同,但是数据都是cpn中的pLanguages
整个思路:
- 子组件将data绑定到
<slot>
中,命名为”data” - 父组件使用
<template>
替换子组件的slot <template>
中使用 slot-scope = “aaa”,并用aaa.data获取<slot>
绑定的data数据