2020-07-16

## 模块化开发

浏览器只支持`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数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335