## 模块化开发
浏览器只支持`ES6`的模块化,其他的需要使用`webpack`处理后才能在浏览器上使用
模块化是将每个`js`文件作为一个模块导入或者导出,解决同时引用多个`js`文件时,变量重名的问题,到后来扩展为非`js`文件也能作为模块进行导入导出。
### export(导出)/import(导入):
```js
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`
```js
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. 文件夹目录:
|-- 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中函数的写法
```js
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`由浏览器执行 --> `ajax`从`API`服务器请求数据,局部更新
3. `SPA`页面:单页富应用,整个页面只有一个`html`页面
* 后端:静态资源服务器只有一个`html+css+js`,`API`服务器继续提供接口
* 前端:前端路由,前端根据不同的`url`从得到的`html+css+js`中抽离出对应的页面
* 过程:输入主页 --> 从静态服务器请求一个(`html`) --> 再请求其他`url` --> 从前面得到的资源抽离出来
## Router:Vue的前端路由
### 基础知识
1. 实现的底层`url`的`hash`更改,`html5`的`history`设置
2. `vue-router`可把一个组件映射成一个`url`页面,每个`.vue`文件就是一个组件(包括`html+css+js`)
3. `router-link`补充:
```
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. 在button(或其他标签)绑定函数
2. 在methods中实现绑定的函数
3. 在绑定的函数中使用this.$router.push('/url')或者replace('/url')
```
5. `vue-router`为每个`vue`组件都绑定了注册的`router`,可以通过`\$router`引用。动态路由中的`\$route`和`\$router`不同,`$route`表示现在活跃的路由。这还是因为所有组件都继承自`vue`的原型,`vue`原型中的属性和方法,`vue`组件都有。
### 路由的懒加载
路由的懒加载:路由中通常定义不同的页面,这页面最后会被打包到一个js文件中,如果一次请求,需要太长时间,可以使用懒加载方式解决
```js
1. 使用箭头函数
{
path:'/home',
component: () => import('../components/Home')
}
//或者使用
const about = () => import('../components/Home') 提取出来
```
### 路由嵌套
路由嵌套:比如在`/home`中分为`/home/news`和`/home/message`
```js
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`组件一样,数据不一样,处理方式
```js
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`中`?`之后的部分
```js
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 等
```
### 路由守卫
路由守卫: 由一个路由跳转到另一个路由的过程,我们可以在里面实现一些动作
```js
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`(非激活)
```html
<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基本使用
```js
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核心:将异步请求代码和业务代码分离
```js
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`中都获取到数据后再操作
```js
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后选择](/imgs/vue-1.png)
2. 使用npm安装vuex
1. 运行`npm i vuex -s`
2. 在项目的根目录下新增一个`store`文件夹,在该`store`文件夹内创建index.js,src的目录结构如下图:
![目录结构图](/imgs/vue-2.png)
3. 初始化`store`中`index.js`中的内容
```js
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`
```js
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的工作流程
![vuex状态管理模式](/imgs/vue-3.png)
首先,`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拿来用
```js
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是该方法在被调用时传递参数使用的
例如,我们编写一个方法,当被执行时,能把下例中的数学值修改:
```js
states:{
math : 99,
english: 90
},
mutations:{
setMathAdd1(state){
state.math ++
},
setEnglishAdd1(){ //可以不传state,默认有一个
english--
}
}
// 组件中调用mutation
this.$store.commit('setMathAdd1')
```
##### Mutations传值用法
在实际开发时,遇到在提交某个`mutation`时需要携带一些参数给方法使用。
```js
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()`
```js
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`实现)。
```js
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实现。
```js
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),每个模块有自己的`states`,`mutations`,`actions`,`getters`,甚至是嵌套子模块。
```js
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,和之前一样。(这里必须子模块和根模块的函数名不同)
```js
this.$store.commit('increment')
this.$store.dispach('aIncrement')
this.$store.getters.doubleCount
```
查看moduleA和moduleB的状态
```js
store.state.a // 可以使用DevTools查看,发现子模块其实就是store.state中的一个对象
store.state.b
```
通过执行 moduleA中aIncrement(context){}函数我们可以得到下面的打印
```js
context是个对象,包含{commit,dispatch,getters,rootGetters,rootState,state}
```
moduleA中aIncrement(context){}函数也可以通过解构的方法写成下面(ES6语法,官网写法)
```js
aIncrement({state,commit,rootState})
```
#### 规范目录结构
如果把整个`store`都放在`index.js`中不合理,所以需要拆分。较为合适的目录结构如下:
```js
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
### 基本用法
```javascript
// 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并发请求
```js
// 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`配置
```js
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`不再维护,修改起来费事,因此可以封装
```js
// 新建一个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拦截器
用于我们在发送每次请求或者得到响应后,进行对应的处理
```js
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`将父组件的数据传给子组件
```js
<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`接收
```js
<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);
}
}
}
```
### 父子组件之间的访问
#### 父组件访问子组件
```html
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) //通过别名获得子组件
}
}
})
```
#### 子组件访问父组件
```js
1. 使用 $father 访问子组件的父组件
console.log(this.$father)
2. 使用 $root 访问子组件的根组件
console.log(this.$root)
```
## Slot插槽
`vue`的`slot`插槽:子组件中使用`<slot>`占用一个位置,父组件调用子组件时,可以用不同的标签(如`button`,`span`)替换
### 匿名插槽
最简单的使用
```html
<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>
```
### 具名插槽
给插槽命名,解决多个插槽时,选择哪个插槽的问题
```html
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并进行不同样式的渲染
```html
//下面是在父组件中引用
<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"
1. 父组件使用`<template>`替换子组件的slot
2. `<template>`中使用 slot-scope = "aaa",并用aaa.data获取`<slot>`绑定的data数据
网友评论