Vue学习(Router、VueX、Axios、组件通信、Slot)

转载请注明出处
个人博客:https://maxusun.github.io/

模块化开发

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

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

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. 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中函数的写法

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补充:
    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文件中,如果一次请求,需要太长时间,可以使用懒加载方式解决

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

路由嵌套

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

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. 在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. 使用 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 等

路由守卫

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

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(非激活)

<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基本使用

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核心:将异步请求代码和业务代码分离

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中都获取到数据后再操作

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,如下图:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ph5SlueT-1594885451639)(/imgs/vue-1.png)]
  2. 使用npm安装vuex
    1. 运行npm i vuex -s
    2. 在项目的根目录下新增一个store文件夹,在该store文件夹内创建index.js,src的目录结构如下图:
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jtVFpdvB-1594885451653)(/imgs/vue-2.png)]
    3. 初始化storeindex.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
      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的工作流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9NZougo-1594885451656)(/imgs/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拿来用
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是该方法在被调用时传递参数使用的
    例如,我们编写一个方法,当被执行时,能把下例中的数学值修改:
states:{
  math : 99,
  english: 90
},
mutations:{
  setMathAdd1(state){   
    state.math ++
  },
  setEnglishAdd1(){   //可以不传state,默认有一个
    english--
  }
}

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

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

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()

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实现)。

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实现。

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,甚至是嵌套子模块。

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,和之前一样。(这里必须子模块和根模块的函数名不同)

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

查看moduleA和moduleB的状态

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

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

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

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

aIncrement({state,commit,rootState})
规范目录结构

如果把整个store都放在index.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

基本用法

// 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并发请求

// 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配置

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封装文件
// 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拦截器

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

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将父组件的数据传给子组件

<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接收

<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. 使用 $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. 使用 $father 访问子组件的父组件
  console.log(this.$father)
2. 使用 $root 访问子组件的根组件
  console.log(this.$root)

Slot插槽

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

匿名插槽

最简单的使用

<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. 匿名插槽的问题:如果有多个插槽,将全部替换
<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并进行不同样式的渲染

//下面是在父组件中引用
<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数据

title: Vue 学习
date: 2020-07-13 21:04:27
tags:

  • Vue
    categoties: Vue
    desctiption: 学习Vue的笔记

模块化开发

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

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

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. 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中函数的写法

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补充:
    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文件中,如果一次请求,需要太长时间,可以使用懒加载方式解决

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

路由嵌套

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

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. 在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. 使用 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 等

路由守卫

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

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(非激活)

<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基本使用

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核心:将异步请求代码和业务代码分离

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中都获取到数据后再操作

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,如下图:
    在这里插入图片描述
  2. 使用npm安装vuex
    1. 运行npm i vuex -s
    2. 在项目的根目录下新增一个store文件夹,在该store文件夹内创建index.js,src的目录结构如下图:
      在这里插入图片描述
    3. 初始化storeindex.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
      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组件如果调用某个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拿来用
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是该方法在被调用时传递参数使用的
    例如,我们编写一个方法,当被执行时,能把下例中的数学值修改:
states:{
  math : 99,
  english: 90
},
mutations:{
  setMathAdd1(state){   
    state.math ++
  },
  setEnglishAdd1(){   //可以不传state,默认有一个
    english--
  }
}

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

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

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()

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实现)。

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实现。

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,甚至是嵌套子模块。

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,和之前一样。(这里必须子模块和根模块的函数名不同)

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

查看moduleA和moduleB的状态

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

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

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

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

aIncrement({state,commit,rootState})
规范目录结构

如果把整个store都放在index.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

基本用法

// 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并发请求

// 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配置

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封装文件
// 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拦截器

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

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将父组件的数据传给子组件

<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接收

<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. 使用 $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. 使用 $father 访问子组件的父组件
  console.log(this.$father)
2. 使用 $root 访问子组件的根组件
  console.log(this.$root)

Slot插槽

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

匿名插槽

最简单的使用

<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. 匿名插槽的问题:如果有多个插槽,将全部替换
<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并进行不同样式的渲染

//下面是在父组件中引用
<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数据
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaXuwl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值