模块化开发

浏览器只支持ES6的模块化,其他的需要使用webpack处理后才能在浏览器上使用
模块化是将每个js文件作为一个模块导入或者导出,解决同时引用多个js文件时,变量重名的问题,到后来扩展为非js文件也能作为模块进行导入导出。

export(导出)/import(导入):

1
2
3
4
5
6
7
8
9
1. html中引用时,添加引用类型
<script src="aa.js" type="module"></script>
2. aa.js中使用export导出
export {变量1,函数1,类1,……}
export 变量2
export 函数2
……
1. bb.js中引用
import {变量1,函数1,类1,……} from "./aa.js" # 和导出的名字一样

export default/import:

导入者可以对导出的内容重新命名,只能有一个default

1
2
3
4
5
6
1. html中引用时,添加引用类型
<script src="aa.js" type="module"></script>
2. aa.js中使用export导出
export default 变量1
3. bb.js中引用
import 变量1_1 from "./aa.js" # 和导出的名字不一样

其他

  1. 统一全部导入:import * as name from './aaa.js',通过name.变量name.函数name.类取对应数据
  2. import后不写路径的话是从mode_modules中直接引用模块

webpack

开发代码 –> webpack处理下 –> 可部署。不然有些文件浏览器不支持开发代码的语法

  1. 核心:让我们能进行模块化开发,并帮助我们处理模块间的依赖关系。不仅打包JavaScript文件。css、image、json等都能当作模块来使用
  2. 用法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1. 文件夹目录:
    |-- 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"管理了很多包

配置文件

  1. vue.config.js配置:
    • alias配置别名:默认 '@':'src',这样引用的时候,直接使用别名就行了
  2. .editorconfig: 配置一些代码风格等,使用的时候前面加个 ~

ES6中函数的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 定义函数的方式
const aaa = function(){}
2. 对象字面中定义函数
const obj = {
bbb: function(){}
bbb(){}
}
3. ES6中的箭头函数
const ccc = (参数) => {}
const ccc = function(){}

一个参数的时候可以去() const ccc = num => {}
一行代码的时候可以去{} const mul = (num1,num2) => num1 * num2
箭头函数的this向外层作用域中一层层查找this,直到有this的定义。函数有作用域,箭头函数没有作用域

网页开发的发展路程

  1. JSP阶段:后端渲染:请求页面时,后端返回整个被渲染好的页面(html+css+js),然后整体返回。后端路由:在后端将一个url和一个页面映射
  2. 前后端分离:
    • 后端:不进行渲染,只负责提供数据,后端分为静态资源服务器和提供API接口的服务器
    • 后端路由:仍然是后端路由阶段
    • 过程:输入url –> 从静态服务器请求(html+css+js) –> js由浏览器执行 –> ajaxAPI服务器请求数据,局部更新
  3. SPA页面:单页富应用,整个页面只有一个html页面
    • 后端:静态资源服务器只有一个html+css+jsAPI服务器继续提供接口
    • 前端:前端路由,前端根据不同的url从得到的html+css+js中抽离出对应的页面
    • 过程:输入主页 –> 从静态服务器请求一个(html) –> 再请求其他url –> 从前面得到的资源抽离出来

Router:Vue的前端路由

基础知识

  1. 实现的底层urlhash更改,html5history设置
  2. vue-router可把一个组件映射成一个url页面,每个.vue文件就是一个组件(包括html+css+js)
  3. router-link补充:
    1
    2
    3
    tag: 指定<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'
  4. 使用原生标签,不用router-link做路由跳转:
    1
    2
    3
    1. 在button(或其他标签)绑定函数
    2. 在methods中实现绑定的函数
    3. 在绑定的函数中使用this.$router.push('/url')或者replace('/url')
  5. vue-router为每个vue组件都绑定了注册的router,可以通过\$router引用。动态路由中的\$route\$router不同,$route表示现在活跃的路由。这还是因为所有组件都继承自vue的原型,vue原型中的属性和方法,vue组件都有。

路由的懒加载

路由的懒加载:路由中通常定义不同的页面,这页面最后会被打包到一个js文件中,如果一次请求,需要太长时间,可以使用懒加载方式解决

1
2
3
4
5
6
7
1. 使用箭头函数
{
path:'/home',
component: () => import('../components/Home')
}
//或者使用
const about = () => import('../components/Home') 提取出来

路由嵌套

路由嵌套:比如在/home中分为/home/news/home/message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 在home下创建子路由
{
path: '/home',
component: Home,
children:[
{
path: '',
redirect:'news'
},
{
path: 'news', //这里不用加 /
component: News
},
{
path: ‘messages’, //不用加 /
component: Messages
}
]
}
2. 在Home.vue中使用router-link注册,路径要写全,/home/news 和 /home/messages

动态路由

动态路由:对于用户/user/zhangsan/user/lisi组件一样,数据不一样,处理方式

1
2
3
4
5
6
7
8
9
10
11
1. 在router中配置
{
path: '/user/:id', // id只是个命名,后面用于取
component:User
}
1. 在router-link中使用
<router-link to="/user/123">用户</router-link>
//实际开发中可以使用v-bind和字符串拼接,跳转到想去的界面 :to="'/user/'+userId",userId是在data中获得的
1. 如何获得这个 123
第一种: <h2>{{$route.params.id}}
第二种: 在method中使用函数绑定后再return,然后在 h2 中调用函数

url参数传递

url参数传递:除了上面的动态路由,也可以使用url中的query参数,query就是url?之后的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 使用 router-link 
<router-link
:to="{
path: 'profile/' + 123,
query: {name:'test',age:18}
}"
></router-link>
1. 使用button,然后绑定方法
toProfile(){
this.$router.push({
path: 'profile/' + 123,
query: {name:'test',age:18}
})
}
//要想获得query中的东西,和动态路由差不多,使用 $route. query.name 等

路由守卫

路由守卫: 由一个路由跳转到另一个路由的过程,我们可以在里面实现一些动作

1
2
3
4
5
6
beforeEach(function(to,from,next){
//将html的title更改为对应页面的。matched[0]:为了处理多级路由,多级路由属性都存在matched里面
//meta: 每个route的属性都存在meta中。meta是个数组,和path和component一级
document.title = to.matched[0].meta.title
next() // next必须有
})

keep-alive

keep-alive:组件创建后不被销毁。使用keep-alive的组件有两个方法activated(激活)和deactivated(非激活)

1
2
3
4
5
6
<keep-alive>
<router-view/>
</keep-alive>
可在 keep-alive 中使用include和exclude来设置只用在哪些组件上。内容是正则的形式,可为组件的name属性,name属性在.vue中定义,比如 <keep-alive exclude="Profile,User"></keep-alive>

<router-view>只是一个占位的,其具体作用是:一个A界面有一个<router-view>,A里面有2个路由,可以跳到B或者C,得到的B或者C组件界面替换掉<router-view>

Promise

promise基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
new Promise((resolve,reject)=>{ // resolve和reject是选的,不用的话可以不传
resolve("Hellor World") //resolve中的Hello World会入到then里面的data中,然后转到then中执行
reject("error Message") //reject中的error Message会传入到catch里面的data中,然后转到catch中执行
}).then((data)=>{
console.log(data);
}).catch(err=>{
console.log(err);
})

// 等价写法
new Promise((resolve,reject)=>{
resolve("Hello World")
reject("Error message")
}).then(data=>{ // then中传入两个参数(函数),第一个是resolve执行的,第二个是reject执行的
console.log(data);
},(err)=>{
console.log(err);
})

Promise核心

Promise核心:将异步请求代码和业务代码分离

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
setTimeout(()=>{
console.log("业务逻辑代码100行");
setTimeout(()=>{
console.log("嵌套1业务代码100行");
setTimeout(()=>{
console.log("嵌套2业务代码100行");
},1000)
},1000)
},1000)
// 上面的嵌套模式可以转为下面的链式
new Promise((resolve)=>{
setTimeout(()=>{
resolve("获取的数据")
},1000)
}).then((data)=>{
console.log(data);
console.log("业务逻辑代码100行");

return new Promise((resolve)=>{
setTimeout(()=>{
resolve()
},1000)
})
}).then(()=>{
console.log("嵌套1业务代码100行");

return new Promise((resolve)=>{
setTimeout(()=>{
resolve()
},1000)
})
}).then(()=>{
console.log("嵌套2业务代码100行");
})

Promise的all方法

场景:从两个(多个)url中都获取到数据后再操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Promise.all([
new Promise((resolve, reject) => {
$.ajax({
url:'url1',
success:function (data1) {
resolve(data1)
}
})
}),
new Promise((resolve, reject) => {
$.ajax({
url:'url1',
success:function (data2) {
resolve(data2)
}
})
})
]).then(results=>{
results[0] //data1
results[1] //data2
})

Vuex

vuex对所有组件进行统一管理,使管理的组件之间能够获得其他组件的状态和属性。统一管理,便于维护。

使用方式(两种)

  1. 在创建vue项目的时候直接勾选使用vuex,如下图:
    vue-1.png
  2. 使用npm安装vuex
    1. 运行npm i vuex -s
    2. 在项目的根目录下新增一个store文件夹,在该store文件夹内创建index.js,src的目录结构如下图:
      vue-2.png
    3. 初始化storeindex.js中的内容
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      import Vue from 'vue'
      import Vuex from 'vuex'

      Vue.use(Vuex)

      export default new Vuex.Store({
      state: {
      },
      mutations: {
      },
      getters:{

      },
      actions: {
      },
      modules: {
      }
      })
    4. store挂载到当前项目的Vue实例当中去,打开main.js
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import 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-3.png
首先,Vue组件如果调用某个Vuex的方法过程中需要向后端请求时或者说出现异步操作时,需要dispatch VueXactions的方法,以保证数据的同步。可以说,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
    15
    states:{
    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
    15
    states:{
    math : 99,
    english: 90
    },
    mutations:{
    setMathAdd1(state){
    state.math ++
    },
    setEnglishAdd1(){ //可以不传state,默认有一个
    english--
    }
    }

    // 组件中调用mutation
    this.$store.commit('setMathAdd1')
Mutations传值用法

在实际开发时,遇到在提交某个mutation时需要携带一些参数给方法使用。

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
states:{
math : 99,
english: 90
},
mutations:{
setMath(state,payload){
state.math = payload
},
setAll(state,payload){ //可以不传state,默认有一个
state.math = payload.math
state.english = payload.english
}
}

// 单值提交
this.$store.commit('setMath',80)

// 多值提交,建议使用对象,下面两种写法等价
this.$store.commit('setAll',{math:80,english:78})
this.$store.commit({
type:'edit',
payload:{
math:80,
english:78
}
})
增删state中的成员

Vuex的sotre中的状态是响应式的,在mutations中都是对已有的数据进行修改是响应式的。但是直接使用delete或者obj['newattr']=attr进行增删数据时,Vue并不能进行实时响应。这是我们就要借助Vue.set()和Vue.delete()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
states:{
math : 99,
english: 90
},
mutations:{
// 对state对象添加一个Chinese数据
addChinese(state,payload){
Vue.set(state,'Chinese',payload)
},
// 删除state中的Chinese数据
delChinese(state){
Vue.delete(state,'Chinese')
}
}

Actions

因为不建议在Mutations中进行异步操作,所以Vuex提供了Actions专门进行异步操作,最终提交mutation方法
Actions中的方法有两个默认参数

  • context:与store实例具有相同方法和属性的context对象(相当于this的指向)
  • payload:和mutations中的payload一样,是传入参数

假如我们有一个需求1:异步修改state中的数据。分析这个需求,需要一个异步操作(必须放到action中),一个修改state数据的操作(必须通过mutations中的mutation实现)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
states:{
math : 99,
english: 90
},
mutations:{
setMath(state,payload){
state.math = payload
},
},
actions:{
aSetMath(context,payload){
setTimeout(()=>{
context.commit('setMath',payload)
},1000)
}
}

// 其他组件调用aSetMath函数
this.$store.dispatch('aSetMath',88)

假如我们有一个需求2:在需求1的基础上,如果异步操作执行成功,通知调用的组件执行相应的函数。这时我们使用Promise实现。

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
states:{
math : 99,
english: 90
},
mutations:{
setMath(state,payload){
state.math = payload
},
},
actions:{
aSetMath(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('setMath',payload)
resolve("成功解决问题")
},1000)
})
}
}

// 其他组件调用aSetMath函数
this.$store.dispatch('aSetMath',88)
.then((data)=>{
console.log(data)
})

Models

如果项目十分庞大,状态很多时,我们可以采用模块化管理。为了解决这个问题,Vuex允许我们将store分割成模块(module),每个模块有自己的statesmutationsactionsgetters,甚至是嵌套子模块。

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
const moduleA={
state:{
count: 0
},
mutations:{
increment(state){ // 这个state是moduleA的state
state.count++
}
},
actions:{
aIncrement(context){ // 这个context是moduleA的context
console.log(context)
console.log(context.state) // 局部模块状态
console.log(context.rootState) // 根节点状态
}
},
getters:{
doubleCount(state,getters,rootState){ //这个state和getter是moduleA的state和getters。rootState是指向根节点
return state.count * 2
}
}
}

const moduldB={
}

const store = new Vuex.Store({
state:{
gCount: 111
},
modules:[
a : moduleA,
b : moduleB
]
})

其他组件调用moduleA的mutation、getter、action,和之前一样。(这里必须子模块和根模块的函数名不同)

1
2
3
this.$store.commit('increment')
this.$store.dispach('aIncrement')
this.$store.getters.doubleCount

查看moduleA和moduleB的状态

1
2
store.state.a         // 可以使用DevTools查看,发现子模块其实就是store.state中的一个对象
store.state.b

通过执行 moduleA中aIncrement(context){}函数我们可以得到下面的打印

1
context是个对象,包含{commit,dispatch,getters,rootGetters,rootState,state}

moduleA中aIncrement(context){}函数也可以通过解构的方法写成下面(ES6语法,官网写法)

1
aIncrement({state,commit,rootState})

规范目录结构

如果把整个store都放在index.js中不合理,所以需要拆分。较为合适的目录结构如下:

1
2
3
4
5
6
7
8
9
store:
│ actions.js
│ getters.js
│ index.js //state还是放在index.js中
│ mutations.js
│ mutations_type.js //该项为存放mutaions方法常量的文件,按需要可加入

└─modules
moduleA.js

对应的内容存放在对应的文件中,并使用export default{}导出,在index.js中引用。使用store:在index.js中存放并导出store

Axios:Axios实现基础是Promise

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// axios(config)用法
axios({
url: 'http://123.207.32.32:8000/home/data?type=sell&page=3',
method: 'get',
// params,专门针对get请求的参数拼接。post方法使用data
params: {
type: 'sell',
page: 3
}
}).then(res=>{
console.log(res);
console.log(res.data);
})
// 和上面等价,同样有 axios.delete(url[,config]),head,post,put,patch函数
axios.get('http://123.207.32.32:8000/home/data?type=sell&page=3').then(res=>{console.log(res);})

axios并发请求

1
2
3
4
5
6
7
8
9
10
11
12
// axios 发送并发请求,和Promise一样
axios.all([
axios({
url:'httpbin.org'
}),
axios({
url:'http://123.207.32.32:8000/home/multidata'
})]
).then(results=>{
console.log(results[0]);
console.log(results[1]);
})

aixos默认配置

使用axios.defaults配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
axios.defaults.baseURL = 'http://123.207.32.32:8080'
axios.get("/home/multidata").then(res=>console.log(res))

// 上面进行axios默认配置,但是配置之后,如果有多个默认url怎么办:使用axios实例
// 对于不同的默认配置可以使用不同的实例
const axiosInstance = axios.create({
baseURL:'http://123.207.32.32:8000',
timeout:5000,
headers:{
'Content-Type':'application/x-www-form-urlencoded'
}
})
axiosInstance({
url: '/category',
method: 'get'
}).then(res=>
console.log(res)
).catch(err=>{
console.log(err);
})

axios封装

在每个组件中都导入的话,如果axios不再维护,修改起来费事,因此可以封装

1
2
3
4
5
6
7
8
9
10
// 新建一个js封装文件
// import axios from 'axios'
export default function request(option) {
const instance = axios.create({
baseURL:'http://123.207.32.32:8000',
timeout:5000,
headers: ''
})
return instance(option)
}

axios拦截器

用于我们在发送每次请求或者得到响应后,进行对应的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
axiosInstance.interceptors.request.use(config=>{
//1. 比如config中的一些信息不符合服务器的要求
//2. 比如每次发送网络请求时,都希望在界面显示一个正在请求的图标
//3. 默写网站请求(比如登录(token)),必须携带一些特殊的信息
console.log("来到了request拦截success中"); //这个因为还没有发送出去,所以得到的是config
return config; // 如果不return config,这个发送将会失败,拦截器是个中间件
},error => {
console.log("来到了request拦截failure中");
return error
})

axiosInstance.interceptors.response(res=>{
console.log("来到了request拦截success中");
return res.data
},error=>{
console.log("来到了request拦截failure中");
return error
})

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
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
<div id="father">
<!-- 父组件接收到子组件的childevent事件后,做出eventget的响应 -->
<cpn @childevent="eventget"></cpn>
</div>
<template id="child">
<div>
<p>我是子组件</p>
</div>
</template>
const child = {
template:'#cpn',
data: {return{}},
created:{
this.$emit("childevent",'我是子组件的事件')
}
}

const father = {
el:'#father',
data:{return{}},
component: {cpn},
methods:{
enventget(str){
console.log(str);
}
}
}

父子组件之间的访问

父组件访问子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 使用 $children 访问子组件
this.$children //得到的是一个组件数组
2. 使用 $refs 访问子组件
<div id = "app">
<cpn></cpn>
<my-cpn ref="aaa"></my-cpn> // 相当于给my-cpn组件起个别名
<cpn></cpn>
</div>
const app = new Vue({
components:{cpn,my-cpn},
created:{
printchildren(){
console.log(this.$refs.aaa) //通过别名获得子组件
}
}
})

子组件访问父组件

1
2
3
4
1. 使用 $father 访问子组件的父组件
console.log(this.$father)
2. 使用 $root 访问子组件的根组件
console.log(this.$root)

Slot插槽

vueslot插槽:子组件中使用<slot>占用一个位置,父组件调用子组件时,可以用不同的标签(如buttonspan)替换

匿名插槽

最简单的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">              //父组件app,并在父组件里面使用子组件cpn
<cpn><span>哈哈哈</span> </cpn>
<cpn><i>呵呵呵</i></cpn>
<cpn></cpn>
</div>
<template id = "cpn"> //子组件cpn的template定义
<div>
<h2>我是组件</h2>
<slot><button>按钮</button></slot>
</div>
</template>
显示的结果:
<div>
<h2>我是组件</h2>
<span>哈哈哈</span>
<i>呵呵呵</i>
<button>按钮</button>
</div>

具名插槽

给插槽命名,解决多个插槽时,选择哪个插槽的问题

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
1. 匿名插槽的问题:如果有多个插槽,将全部替换
<div id="app"> //父组件app,并在父组件里面使用子组件cpn
<cpn><span>哈哈哈</span></cpn>
</div>
<template id = "cpn"> //子组件cpn的template定义
<div>
<slot></slot>
<slot></slot>
</div>
</template>
显示的结果:
<div>
<span>哈哈哈</span>
<span>哈哈哈</span>
</div>

2. 具名插槽的使用:多个插槽时,给插槽取名字
<div id="app"> //父组件app,并在父组件里面使用子组件cpn
<cpn><span slot="right">哈哈哈</span></cpn>
<cpn><i slot="left">呵呵呵</i></cpn>
</div>
<template id = "cpn"> //子组件cpn的template定义
<div>
<slot name="left"></slot>
<slot name="right"></slot>
</div>
</template>
显示的结果:
<div>
<span>呵呵呵</span>
<span>哈哈哈</span>
</div>

作用域插槽

父组件获得子组件的data并进行不同样式的渲染

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
//下面是在父组件中引用
<div>
<cpn></cpn> // 引用子组件1处
<cpn> // 引用子组件2处
<template slot-scope="slot">
<span v-for="item in slot.data">{{item}} - </span>
</template>
</cpn>
</div>
// 下面是子组件的定义
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
const cpn = new Vue({
data(){
return {
pLanguages:['Java','C','C++','Python']
}
}
})

显示的结果:引用组件1处和2处展现的方式不同,但是数据都是cpn中的pLanguages
整个思路:

  1. 子组件将data绑定到<slot>中,命名为”data”
  2. 父组件使用<template>替换子组件的slot
  3. <template>中使用 slot-scope = “aaa”,并用aaa.data获取<slot>绑定的data数据